# -*- coding: utf-8 -*- # # Copyright 2013 whiteleaf. All rights reserved. # require "fileutils" require_relative "../downloader" require_relative "../novelconverter" require_relative "../inventory" require_relative "../kindlestrip" module Command class Convert < CommandBase def self.oneline_help "小説を変換します。管理小説以外にテキストファイルも変換可能" end attr_accessor :device, :converted_txt_path @@sending_error_list = [] def self.display_sending_error_list puts puts "-" * 79 puts "・送信失敗リスト" puts @@sending_error_list puts puts "上記のファイルの送信に失敗しました。".termcolor puts "送信出来なかった原因を解消し、send コマンドを実行して下さい。" @@sending_error_list.clear if $stdin.tty? && Narou.web?.! puts puts "(何かキーを押して下さい)" $stdin.getch end end def self.exists_sending_error_list? @@sending_error_list.empty?.! end def initialize @argument_target_type = :file super(" [ ...] [options]") @opt.separator <<-EOS ・指定した小説を縦書き用に整形及びEPUB、MOBIに変換します。 ・変換したい小説のNコード、URL、タイトルもしくはIDを指定して下さい。 IDは #{@opt.program_name} list を参照して下さい。 ・一度に複数の小説を指定する場合は空白で区切って下さい。 ※-oオプションがない場合、[作者名] 小説名.txtが小説の保存フォルダに出力されます ・管理小説以外にもテキストファイルを変換出来ます。 テキストファイルのファイルパスを指定します。 ※複数指定した場合に-oオプションがあった場合、ファイル名に連番がつきます。 ・MOBI化する場合は narou setting device=kindle をして下さい。 ・device=kobo の場合、.kepub.epub を出力します。 Examples: narou convert n9669bk narou convert http://ncode.syosetu.com/n9669bk/ narou convert 異世界迷宮で奴隷ハーレムを narou convert 1 -o "ハーレム -変換済み-.txt" narou convert mynovel.txt --enc sjis Options: EOS @opt.on("-o FILE", "--output FILE", "出力ファイル名を指定する。フォルダパス部分は無視される") { |filename| @options["output"] = filename } @opt.on("-e ENCODING", "--enc ENCODING", "テキストファイル指定時の文字コードを指定する。デフォルトはUTF-8") { |encoding| encoding = "utf-8" if encoding =~ /UTF8/i @options["encoding"] = encoding } @opt.on("--no-epub", "AozoraEpub3でEPUB化しない") { @options["no-epub"] = true } @opt.on("--no-mobi", "kindlegenでMOBI化しない") { @options["no-mobi"] = true } @opt.on("--no-strip", "MOBIをstripしない") { @options["no-strip"] = true } @opt.on("--no-zip", "i文庫用のzipファイルを作らない") { @options["no-zip"] = true } @opt.on("--no-open", "出力時に保存フォルダを開かない") { @options["no-open"] = true } @opt.on("-i", "--inspect", "小説状態の調査結果を表示する") { @options["inspect"] = true } @opt.on("-v", "--verbose", "AozoraEpub3, kindlegen の標準出力を全て表示する") { @options["verbose"] = true } @opt.on("--ignore-default", "settingコマンドのdefault系設定を無視する") { @options["ignore-default"] = true } @opt.on("--ignore-force", "settingコマンドのforce系設定を無視する") { @options["ignore-force"] = true } @opt.separator <<-EOS Configuration: --no-epub, --no-mobi, --no-strip, --no-open , --inspect は narou setting コマンドで恒常的な設定にすることが可能です。 convert.copy-to を設定すれば変換したEPUB/MOBIを指定のフォルダに自動でコピー出来ます。 device で設定した端末が接続されていた場合、対応するデータを自動送信します。 詳しくは narou setting --help を参照して下さい。 EOS end def execute(argv) super if argv.empty? puts @opt.help return end @output_filename = @options["output"] if @output_filename @ext = File.extname(@output_filename) @basename = File.basename(@output_filename, @ext) else @basename = nil end if @options["encoding"] @enc = Encoding.find(@options["encoding"]) rescue nil unless @enc error "--enc で指定された文字コードは存在しません。sjis, eucjp, utf-8 等を指定して下さい" return end end @multi_device = @options["multi-device"] device_names = if @multi_device @multi_device.split(",").map(&:strip).map(&:downcase).select { |name| Device.exists?(name).tap { |this| unless this error "[convert.multi-device] #{name} は有効な端末名ではありません" end } } else [nil] # nil で device 設定が読まれる end # kindle用のmobiを作る過程でepubが作成され、上書きされてしまうので、最初に作るようにする kindle = device_names.delete("kindle") device_names.unshift(kindle) if kindle if @multi_device && device_names.empty? error "有効な端末名がひとつもありませんでした" exit Narou::EXIT_ERROR_CODE end device_names.each do |name| @device = Narou.get_device(name) if name puts ">> #{@device.display_name}用に変換します".termcolor end self.extend(@device.get_hook_module) if @device hook_call(:change_settings) convert_novels(argv) end # device の設定に戻す if @multi_device device = Narou.get_device force_change_settings_function(device.get_relative_variables) if device end end def change_settings return unless @device if @multi_device force_change_settings_function(@device.get_relative_variables) end end def convert_novels(argv) tagname_to_ids(argv) argv.each.with_index(1) do |target, i| @target = target @novel_data = nil Helper.print_horizontal_rule if i > 1 if @basename @basename << " (#{i})" if argv.length > 1 @output_filename = @basename + @ext end if File.file?(target.to_s) res = convert_txt(target) else @argument_target_type = :novel unless Downloader.novel_exists?(target) error "#{target} は存在しません" next end res = NovelConverter.convert(target, { output_filename: @output_filename, display_inspector: @options["inspect"], ignore_force: @options["ignore-force"], ignore_default: @options["ignore-default"], }) @novel_data = Downloader.get_data_by_target(target) end next unless res @converted_txt_path = res[:converted_txt_path] @use_dakuten_font = res[:use_dakuten_font] ebook_file = hook_call(:convert_txt_to_ebook_file) next if ebook_file.nil? if ebook_file copied_file_path = copy_to_converted_file(ebook_file) send_file_to_device(ebook_file) end if @options["no-open"].! && Narou.web?.! Helper.open_directory(File.dirname(@converted_txt_path), "小説の保存フォルダを開きますか") end end rescue Interrupt puts puts "変換を中断しました" exit Narou::EXIT_ERROR_CODE end # # 直接指定されたテキストファイルを変換する # def convert_txt(target) return NovelConverter.convert_file(target, { encoding: @enc, output_filename: @output_filename, display_inspector: @options["inspect"], ignore_force: @options["ignore-force"], ignore_default: @options["ignore-default"], }) rescue ArgumentError => e if e.message =~ /invalid byte sequence in UTF-8/ error "テキストファイルの文字コードがUTF-8ではありません。" + "--enc オプションでテキストの文字コードを指定して下さい" warn "(#{e.message})" return nil else raise end rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError warn "#{target}:" error "テキストファイルの文字コードは#{@options["encoding"]}ではありませんでした。" + "正しい文字コードを指定して下さい" return nil end # # 変換された整形済みテキストファイルをデバイスに対応した書籍データに変換する # def convert_txt_to_ebook_file return NovelConverter.convert_txt_to_ebook_file(@converted_txt_path, { use_dakuten_font: @use_dakuten_font, device: @device, verbose: @options["verbose"], no_epub: @options["no-epub"], no_mobi: @options["no-mobi"], no_strip: @options["no-strip"] }) end class NoSuchDirectory < StandardError; end # # convert.copy-to で指定されたディレクトリに書籍データをコピーする # def copy_to_converted_file(src_path) copy_to_dir = get_copy_to_directory return nil unless copy_to_dir FileUtils.copy(src_path, copy_to_dir) copied_file_path = File.join(copy_to_dir, File.basename(src_path)) puts copied_file_path.encode(Encoding::UTF_8) + " へコピーしました" copied_file_path rescue NoSuchDirectory => e error "#{e.message} はフォルダではないかすでに削除されています。コピー出来ませんでした" nil end # # 書籍ファイルのコピー先を取得 # # copy-to が設定されていなければ nil を返す。 # 存在しないディレクトリだった場合は例外を投げる # def get_copy_to_directory # 2.1.0 から convert.copy_to から convert.copy-to へ名称が変更された # (互換性維持のため、copy_to も使えるようにはしておく) copy_to_dir = @options["copy-to"] || @options["copy_to"] return nil unless copy_to_dir unless File.directory?(copy_to_dir) raise NoSuchDirectory, copy_to_dir end # deviceごとにフォルダ振り分けの処理 if !@options["copy-to-grouping"] || !@device return copy_to_dir end copy_to_dir_with_device = File.join(copy_to_dir, @device.display_name) unless File.directory?(copy_to_dir_with_device) FileUtils.mkdir(copy_to_dir_with_device) end copy_to_dir_with_device end private :get_copy_to_directory def send_file_to_device(ebook_file) if @device && @device.physical_support? && @device.connecting? && File.extname(ebook_file) == @device.ebook_file_ext if @argument_target_type == :novel if Send.execute!([@device.name, @target]) > 0 @@sending_error_list << ebook_file end else puts @device.name + "へ送信しています" copy_to_path = nil begin copy_to_path = @device.copy_to_documents(ebook_file) rescue Device::SendFailure end if copy_to_path puts copy_to_path.encode(Encoding::UTF_8) + " へコピーしました" else error "送信に失敗しました" @@sending_error_list << ebook_file end end end end end end