epub_tools 0.5.0 → 0.9.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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +3 -8
  3. data/.gitignore +0 -1
  4. data/.tool-versions +1 -1
  5. data/CLAUDE.md +20 -24
  6. data/Gemfile +5 -8
  7. data/Gemfile.lock +60 -54
  8. data/README.md +65 -25
  9. data/bin/epub-tools +1 -3
  10. data/epub_tools.gemspec +3 -4
  11. data/lib/epub_tools/add_chapters.rb +26 -14
  12. data/lib/epub_tools/append_book.rb +74 -0
  13. data/lib/epub_tools/book_builder.rb +114 -0
  14. data/lib/epub_tools/chapter_order.rb +33 -0
  15. data/lib/epub_tools/chapter_validator.rb +20 -7
  16. data/lib/epub_tools/cli.rb +121 -22
  17. data/lib/epub_tools/compile_book.rb +12 -126
  18. data/lib/epub_tools/epub_initializer.rb +93 -99
  19. data/lib/epub_tools/pack_ebook.rb +2 -14
  20. data/lib/epub_tools/pdf_converter.rb +113 -0
  21. data/lib/epub_tools/pdf_layout.rb +116 -0
  22. data/lib/epub_tools/set_cover.rb +116 -0
  23. data/lib/epub_tools/split_chapters.rb +35 -70
  24. data/lib/epub_tools/style_finder.rb +11 -40
  25. data/lib/epub_tools/unpack_ebook.rb +2 -20
  26. data/lib/epub_tools/version.rb +1 -1
  27. data/lib/epub_tools/xhtml_cleaner.rb +4 -7
  28. data/lib/epub_tools/xhtml_extractor.rb +1 -7
  29. data/lib/epub_tools.rb +4 -0
  30. data/test/add_chapters_test.rb +134 -0
  31. data/test/append_book_test.rb +127 -0
  32. data/test/chapter_validator_test.rb +39 -0
  33. data/test/cli_commands_test.rb +9 -0
  34. data/test/cli_test.rb +40 -3
  35. data/test/compile_book_test.rb +15 -2
  36. data/test/epub_initializer_test.rb +18 -0
  37. data/test/pdf_converter_test.rb +69 -0
  38. data/test/pdf_layout_test.rb +57 -0
  39. data/test/set_cover_test.rb +68 -0
  40. data/test/split_chapters_test.rb +84 -9
  41. data/test/style_finder_test.rb +3 -9
  42. data/test/test_helper.rb +1 -1
  43. data/test/unpack_ebook_test.rb +1 -1
  44. data/test/xhtml_cleaner_test.rb +1 -4
  45. metadata +17 -34
  46. data/lib/epub_tools/cli/command_options_configurator.rb +0 -115
  47. data/lib/epub_tools/cli/command_registry.rb +0 -49
  48. data/lib/epub_tools/cli/option_builder.rb +0 -166
  49. data/lib/epub_tools/cli/runner.rb +0 -113
  50. data/lib/epub_tools/compile_workspace.rb +0 -40
  51. data/lib/epub_tools/epub_configuration.rb +0 -33
  52. data/lib/epub_tools/epub_file_writer.rb +0 -57
  53. data/lib/epub_tools/epub_metadata_builder.rb +0 -92
  54. data/lib/epub_tools/xhtml_generator.rb +0 -71
  55. data/test/cli/command_registry_test.rb +0 -68
  56. data/test/cli/option_builder_test.rb +0 -183
  57. data/test/cli/runner_test.rb +0 -91
  58. data/test/compile_workspace_test.rb +0 -55
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require_relative 'loggable'
5
+ require_relative 'xhtml_extractor'
6
+ require_relative 'split_chapters'
7
+ require_relative 'pdf_converter'
8
+ require_relative 'add_chapters'
9
+ require_relative 'pack_ebook'
10
+ require_relative 'chapter_validator'
11
+
12
+ module EpubTools
13
+ # Base class for book-building workflows (compile and append). Sources can be EPUBs (split into chapters)
14
+ # and/or chapter PDFs (see {PDFConverter}[rdoc-ref:EpubTools::PDFConverter] for the naming convention).
15
+ # Uses template method pattern — subclasses override hooks to customize behavior.
16
+ class BookBuilder
17
+ include Loggable
18
+
19
+ attr_reader :source_dir, :build_dir, :verbose
20
+
21
+ def initialize(options = {})
22
+ @source_dir = options.fetch(:source_dir)
23
+ @build_dir = options[:build_dir] || File.join(Dir.pwd, '.epub_tools_build')
24
+ @verbose = options[:verbose] || false
25
+ end
26
+
27
+ # Run the full build workflow
28
+ # @return [String] Path to the output EPUB file
29
+ def run
30
+ setup_workspace
31
+ prepare_epub
32
+ extract_xhtmls
33
+ split_xhtmls
34
+ convert_pdfs
35
+ validate_chapters
36
+ before_add_chapters
37
+ add_chapters
38
+ pack_epub
39
+ finalize_and_cleanup
40
+ end
41
+
42
+ private
43
+
44
+ # Hook: called before extract/split to set up the EPUB target
45
+ def prepare_epub; end
46
+
47
+ # Hook: called after validation, before adding chapters
48
+ def before_add_chapters; end
49
+
50
+ # Subclasses also define +book_title+ (used when splitting chapters) and +output_path+ (the EPUB to write)
51
+
52
+ def setup_workspace
53
+ FileUtils.rm_rf(build_dir)
54
+ FileUtils.mkdir_p([xhtml_dir, chapters_dir])
55
+ log 'Preparing build directories...'
56
+ end
57
+
58
+ def extract_xhtmls
59
+ log "Extracting XHTML files from EPUBs in '#{source_dir}'..."
60
+ XHTMLExtractor.new(source_dir: source_dir, target_dir: xhtml_dir, verbose: verbose).run
61
+ end
62
+
63
+ def split_xhtmls
64
+ Dir.glob(File.join(xhtml_dir, '*.xhtml')).each { |f| split_xhtml_file(f) }
65
+ end
66
+
67
+ def split_xhtml_file(xhtml_file)
68
+ log "Splitting '#{File.basename(xhtml_file, '.xhtml')}'..."
69
+ SplitChapters.new(
70
+ input_file: xhtml_file, book_title: book_title,
71
+ output_dir: chapters_dir, verbose: verbose
72
+ ).run
73
+ end
74
+
75
+ def convert_pdfs
76
+ return if Dir.glob(File.join(source_dir, '*.pdf')).empty?
77
+
78
+ log "Converting PDFs in '#{source_dir}'..."
79
+ PDFConverter.new(
80
+ source_dir: source_dir, book_title: book_title, output_dir: chapters_dir, verbose: verbose
81
+ ).run
82
+ end
83
+
84
+ def validate_chapters
85
+ ChapterValidator.new(chapters_dir: chapters_dir, verbose: verbose).validate
86
+ end
87
+
88
+ def add_chapters
89
+ log 'Adding chapters to EPUB...'
90
+ AddChapters.new(
91
+ chapters_dir: chapters_dir,
92
+ oebps_dir: epub_oebps_dir,
93
+ verbose: verbose
94
+ ).run
95
+ end
96
+
97
+ def pack_epub
98
+ log "Building EPUB '#{output_path}'..."
99
+ # Expanded here: PackEbook puts a relative path next to epub_dir, inside the build dir that gets deleted
100
+ PackEbook.new(input_dir: epub_dir, output_file: File.expand_path(output_path), verbose: verbose).run
101
+ end
102
+
103
+ def finalize_and_cleanup
104
+ log "Done. Output EPUB: #{File.expand_path(output_path)}"
105
+ FileUtils.rm_rf(build_dir)
106
+ output_path
107
+ end
108
+
109
+ def xhtml_dir = File.join(build_dir, 'xhtml')
110
+ def chapters_dir = File.join(build_dir, 'chapters')
111
+ def epub_dir = File.join(build_dir, 'epub')
112
+ def epub_oebps_dir = File.join(epub_dir, 'OEBPS')
113
+ end
114
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EpubTools
4
+ # Reading order of chapter files: chapter_1, chapter_1_5, chapter_1a, chapter_1b, chapter_2, ..., chapter_epilogue
5
+ module ChapterOrder
6
+ module_function
7
+
8
+ # @param filename [String] A chapter filename, e.g. +chapter_12a.xhtml+
9
+ # @return [Array] A key that sorts chapter filenames in reading order
10
+ def sort_key(filename)
11
+ basename = File.basename(filename, '.xhtml')
12
+ return [Float::INFINITY, ''] if basename == 'chapter_epilogue'
13
+
14
+ if (m = basename.match(/_(\d+)_5\z/))
15
+ [m[1].to_f + 0.5, '']
16
+ else
17
+ [basename[/\d+/].to_f, basename[/\d+([a-z]*)\z/, 1].to_s]
18
+ end
19
+ end
20
+
21
+ # Inserts +node+ into +parent+ before the first chapter that sorts after +filename+, so a chapter added
22
+ # later (e.g. 12a after 13 is already in the book) still lands in reading order.
23
+ # @yield [element] Each existing child element; returns the filename it points to
24
+ def insert(parent, node, filename)
25
+ key = sort_key(filename)
26
+ later = parent.element_children.find do |el|
27
+ href = yield(el)
28
+ href&.start_with?('chapter_') && (sort_key(href) <=> key) == 1
29
+ end
30
+ later ? later.add_previous_sibling(node) : parent.add_child(node)
31
+ end
32
+ end
33
+ end
@@ -12,28 +12,41 @@ module EpubTools
12
12
  @verbose = verbose
13
13
  end
14
14
 
15
+ # Validates that integer chapter numbers form a complete sequence with no gaps.
16
+ # Half-chapters (e.g. chapter_5_5.xhtml) and lettered side chapters (e.g. chapter_5a.xhtml) are
17
+ # recognized but not required.
18
+ # @raise [RuntimeError] if no chapter files are found or if integer chapters have gaps
15
19
  def validate
16
20
  log 'Validating chapter sequence...'
17
21
  nums = extract_chapter_numbers
18
22
  check_sequence_completeness(nums)
19
- log "Chapter sequence is complete: #{nums.first} to #{nums.last}."
23
+ log "Chapter sequence is complete: #{nums.first.floor} to #{nums.last.floor}."
20
24
  end
21
25
 
22
26
  private
23
27
 
24
28
  def extract_chapter_numbers
25
- nums = Dir.glob(File.join(@chapters_dir, '*.xhtml')).map do |file|
26
- if (m = File.basename(file, '.xhtml').match(/_(\d+)\z/))
27
- m[1].to_i
28
- end
29
- end.compact
29
+ nums = Dir.glob(File.join(@chapters_dir, '*.xhtml')).filter_map do |file|
30
+ extract_chapter_number(File.basename(file, '.xhtml'))
31
+ end
30
32
  raise "No chapter files found in #{@chapters_dir}" if nums.empty?
31
33
 
32
34
  nums.sort.uniq
33
35
  end
34
36
 
37
+ def extract_chapter_number(basename)
38
+ if (m = basename.match(/_(\d+)(?:_5|[a-z]+)\z/))
39
+ m[1].to_i + 0.5
40
+ elsif (m = basename.match(/_(\d+)\z/))
41
+ m[1].to_i
42
+ end
43
+ end
44
+
35
45
  def check_sequence_completeness(sorted)
36
- missing = (sorted.first..sorted.last).to_a - sorted
46
+ integers = sorted.select { |n| n == n.to_i }.map(&:to_i)
47
+ return if integers.empty? # e.g. appending only side chapters
48
+
49
+ missing = (integers.first..integers.last).to_a - integers
37
50
  raise "Missing chapter numbers: #{missing.join(' ')}" if missing.any?
38
51
  end
39
52
  end
@@ -1,32 +1,131 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'cli/command_registry'
4
- require_relative 'cli/option_builder'
5
- require_relative 'cli/runner'
3
+ require 'optparse'
4
+ require_relative 'version'
5
+ require_relative 'epub_initializer'
6
+ require_relative 'xhtml_extractor'
7
+ require_relative 'split_chapters'
8
+ require_relative 'pdf_converter'
9
+ require_relative 'add_chapters'
10
+ require_relative 'pack_ebook'
11
+ require_relative 'unpack_ebook'
12
+ require_relative 'set_cover'
13
+ require_relative 'compile_book'
14
+ require_relative 'append_book'
6
15
 
7
16
  module EpubTools
8
- # CLI module - houses the object-oriented command line interface components
17
+ # Command line interface: parses a command's flags and runs the class that implements it
9
18
  module CLI
10
- # Create a new Runner instance configured with all available commands
11
- # @param program_name [String] Name of the program
12
- # @return [CLI::Runner] A configured runner instance
13
- def self.create_runner(program_name = nil)
14
- runner = Runner.new(program_name)
15
- register_all_commands(runner.registry)
16
- runner
19
+ TITLE = ['-t TITLE', '--title TITLE', 'Book title', :title, :required].freeze
20
+ AUTHOR = ['-a AUTHOR', '--author AUTHOR', 'Author name', :author, :required].freeze
21
+ COVER = ['-c PATH', '--cover PATH', 'Cover image file path (optional)', :cover_image].freeze
22
+ BOOK_TITLE = ['-t TITLE', '--title TITLE', 'Book title for HTML <title> tags', :book_title, :required].freeze
23
+ CHAPTERS_OUT = ['-o DIR', '--output-dir DIR', 'Output directory for chapter files (default: ./chapters)',
24
+ :output_dir].freeze
25
+
26
+ # Each command's class, usage summary and flags as [short, long, description, option key, :required].
27
+ # Commands log progress unless run with -q, except those marked <tt>verbose: false</tt>.
28
+ COMMANDS = {
29
+ 'init' => { class: EpubInitializer, summary: 'Initialize a bare-bones EPUB',
30
+ flags: [TITLE, AUTHOR, ['-o DIR', '--output-dir DIR', 'Destination EPUB directory',
31
+ :destination, :required], COVER] },
32
+ 'extract' => { class: XHTMLExtractor, summary: 'Extract XHTML files from EPUBs',
33
+ flags: [['-s DIR', '--source-dir DIR', 'Directory with EPUBs to extract XHTMLs from',
34
+ :source_dir, :required],
35
+ ['-t DIR', '--target-dir DIR',
36
+ 'Directory where the XHTML files will be extracted to', :target_dir, :required]] },
37
+ 'split' => { class: SplitChapters, summary: 'Split XHTML into separate XHTMLs per chapter',
38
+ flags: [['-i FILE', '--input FILE', 'Source XHTML file', :input_file, :required], BOOK_TITLE,
39
+ CHAPTERS_OUT] },
40
+ 'pdf' => { class: PDFConverter, summary: 'Convert chapter PDFs into chapter XHTMLs',
41
+ flags: [['-s DIR', '--source-dir DIR', "Dir of PDFs named '12a Title.pdf'", :source_dir, :required],
42
+ BOOK_TITLE, CHAPTERS_OUT] },
43
+ 'add' => { class: AddChapters, summary: 'Add chapter XHTML files into an EPUB', verbose: false,
44
+ flags: [['-c DIR', '--chapters-dir DIR', 'Chapters directory', :chapters_dir, :required],
45
+ ['-e DIR', '--oebps-dir DIR', 'EPUB OEBPS directory', :oebps_dir, :required]] },
46
+ 'pack' => { class: PackEbook, summary: 'Package an EPUB directory into a .epub file',
47
+ flags: [['-i DIR', '--input-dir DIR', 'EPUB directory to package', :input_dir, :required],
48
+ ['-o FILE', '--output-file FILE', 'Output EPUB file path', :output_file, :required]] },
49
+ 'unpack' => { class: UnpackEbook, summary: 'Unpack an EPUB file into a directory',
50
+ flags: [['-i FILE', '--input-file FILE', 'EPUB file to unpack', :epub_file, :required],
51
+ ['-o DIR', '--output-dir DIR',
52
+ 'Output directory to extract into (default: basename of epub)', :output_dir]] },
53
+ 'cover' => { class: SetCover, summary: 'Add or replace the cover of an EPUB',
54
+ flags: [['-i FILE', '--input-file FILE', 'EPUB to add the cover to', :epub_file, :required],
55
+ ['-c PATH', '--cover PATH', 'Cover image: jpg, png, gif or svg', :cover_image, :required]] },
56
+ 'compile' => { class: CompileBook,
57
+ summary: 'Takes EPUBs and/or chapter PDFs in a dir and compiles them into a single EPUB.',
58
+ flags: [TITLE, AUTHOR,
59
+ ['-s DIR', '--source-dir DIR', 'Directory with EPUBs and/or chapter PDFs',
60
+ :source_dir, :required],
61
+ ['-o FILE', '--output FILE', 'EPUB to create (default: <Book_Title>.epub)',
62
+ :output_file], COVER] },
63
+ 'append' => { class: AppendBook,
64
+ summary: 'Takes EPUBs and/or chapter PDFs in a dir and appends them to an existing EPUB.',
65
+ flags: [['-s DIR', '--source-dir DIR',
66
+ 'Directory with EPUBs and/or chapter PDFs to append', :source_dir, :required],
67
+ ['-t FILE', '--target-epub FILE', 'Existing EPUB file to append to',
68
+ :target_epub, :required]] }
69
+ }.freeze
70
+
71
+ module_function
72
+
73
+ # Runs the command named by the first argument, or prints the version or usage
74
+ # @param args [Array<String>] Command line arguments
75
+ # @return [Object] The command instance that ran
76
+ def run(args)
77
+ if ['-v', '--version'].include?(args[0])
78
+ puts VERSION
79
+ exit
80
+ end
81
+ usage unless COMMANDS.key?(args[0])
82
+
83
+ name, *rest = args
84
+ COMMANDS[name][:class].new(parse(name, rest)).tap(&:run)
85
+ end
86
+
87
+ # @param name [String] Command name
88
+ # @param args [Array<String>] The command's arguments
89
+ # @return [Hash] Options for the command's class; exits with the help text if they are invalid
90
+ def parse(name, args)
91
+ command = COMMANDS.fetch(name)
92
+ options = verbose?(command) ? { verbose: true } : {}
93
+ parser = parser_for(name, command, options)
94
+ parser.parse(args)
95
+ missing = missing_flags(command, options)
96
+ abort "Missing required options: #{missing.join(', ')}\n#{parser}" if missing.any?
97
+
98
+ options
99
+ rescue OptionParser::ParseError => e
100
+ abort "#{e.message}\n#{parser}"
17
101
  end
18
102
 
19
- # Register all available commands with their configurations
20
- # @param registry [CommandRegistry] The command registry to populate
21
- def self.register_all_commands(registry)
22
- registry.register('add', EpubTools::AddChapters, %i[chapters_dir oebps_dir])
23
- registry.register('extract', EpubTools::XHTMLExtractor, %i[source_dir target_dir], { verbose: true })
24
- registry.register('split', EpubTools::SplitChapters, %i[input_file book_title],
25
- { output_dir: './chapters', prefix: 'chapter', verbose: true })
26
- registry.register('init', EpubTools::EpubInitializer, %i[title author destination], { verbose: true })
27
- registry.register('pack', EpubTools::PackEbook, %i[input_dir output_file], { verbose: true })
28
- registry.register('unpack', EpubTools::UnpackEbook, [:epub_file], { verbose: true })
29
- registry.register('compile', EpubTools::CompileBook, %i[title author source_dir], { verbose: true })
103
+ # @return [Array<String>] The long names of required flags that were not given, e.g. +--title+
104
+ def missing_flags(command, options)
105
+ command[:flags].filter_map { |_, long, _, key, required| long.split.first if required && options[key].nil? }
30
106
  end
107
+
108
+ def parser_for(name, command, options)
109
+ OptionParser.new("Usage: #{program_name} #{name} [options]") do |parser|
110
+ command[:flags].each do |short, long, desc, key, required|
111
+ parser.on(short, long, required ? "#{desc} (required)" : desc) { |v| options[key] = v }
112
+ end
113
+ parser.on('-q', '--quiet', 'Run quietly (default: verbose)') { options[:verbose] = false } if verbose?(command)
114
+ parser.on('-h', '--help', 'Print this help') do
115
+ puts parser
116
+ exit
117
+ end
118
+ end
119
+ end
120
+
121
+ def usage
122
+ puts "Usage: #{program_name} COMMAND [options]", 'Commands:'
123
+ COMMANDS.each { |name, command| puts " #{name.ljust(9)} #{command[:summary]}" }
124
+ exit 1
125
+ end
126
+
127
+ def verbose?(command) = command.fetch(:verbose, true)
128
+
129
+ def program_name = File.basename($PROGRAM_NAME)
31
130
  end
32
131
  end
@@ -1,150 +1,36 @@
1
- #!/usr/bin/env ruby
2
1
  # frozen_string_literal: true
3
2
 
4
- require 'fileutils'
5
- require_relative 'loggable'
6
- require_relative 'xhtml_extractor'
7
- require_relative 'split_chapters'
3
+ require_relative 'book_builder'
8
4
  require_relative 'epub_initializer'
9
- require_relative 'add_chapters'
10
- require_relative 'pack_ebook'
11
- require_relative 'compile_workspace'
12
- require_relative 'chapter_validator'
13
5
 
14
6
  module EpubTools
15
- # Orchestrates extraction, splitting, validation, and packaging of book EPUBs
16
- class CompileBook
17
- include Loggable
7
+ # Compiles a new EPUB from source EPUBs by extracting, splitting, and repackaging
8
+ class CompileBook < BookBuilder
9
+ attr_reader :title, :author, :cover_image, :output_file
18
10
 
19
- # Book title
20
- attr_reader :title
21
- # Book author
22
- attr_reader :author
23
- # Path of the input epubs
24
- attr_reader :source_dir
25
- # Optional path to the cover image
26
- attr_reader :cover_image
27
- # Filename for the final epub
28
- attr_reader :output_file
29
- # Optional working directory for intermediate files
30
- attr_reader :build_dir
31
- # Whether to print progress to STDOUT
32
- attr_reader :verbose
33
-
34
- # Initializes the class
35
- # @param options [Hash] Configuration options
36
- # @option options [String] :title Book title (required)
37
- # @option options [String] :author Book author (required)
38
- # @option options [String] :source_dir Path of the input epubs (required)
39
- # @option options [String] :cover_image Optional path to the cover image
40
- # @option options [String] :output_file Filename for the final epub (default: [title].epub)
41
- # @option options [String] :build_dir Optional working directory for intermediate files
42
- # @option options [Boolean] :verbose Whether to print progress to STDOUT (default: false)
43
11
  def initialize(options = {})
12
+ super
44
13
  @title = options.fetch(:title)
45
14
  @author = options.fetch(:author)
46
- @source_dir = options.fetch(:source_dir)
47
15
  @cover_image = options[:cover_image]
48
16
  @output_file = options[:output_file] || default_output_file
49
- @build_dir = options[:build_dir] || File.join(Dir.pwd, '.epub_tools_build')
50
- @verbose = options[:verbose] || false
51
- @workspace = CompileWorkspace.new(@build_dir)
52
- end
53
-
54
- # Run the full compile workflow
55
- def run
56
- setup_workspace
57
- extract_xhtmls
58
- split_xhtmls
59
- validate_chapters
60
- initialize_epub
61
- add_chapters
62
- pack_epub
63
- finalize_and_cleanup
64
17
  end
65
18
 
66
19
  private
67
20
 
68
- def setup_workspace
69
- @workspace.clean
70
- log "Cleaning build directory #{@build_dir}..."
71
- @workspace.prepare_directories
72
- log 'Preparing build directories...'
73
- end
21
+ def book_title = @title
74
22
 
75
- def finalize_and_cleanup
76
- log "Done. Output EPUB: #{File.expand_path(output_file)}"
77
- @workspace.clean
78
- output_file
79
- end
80
-
81
- def default_output_file
82
- "#{title.gsub(' ', '_')}.epub"
83
- end
84
-
85
- def extract_xhtmls
86
- log "Extracting XHTML files from epubs in '#{source_dir}'..."
87
- XHTMLExtractor.new({
88
- source_dir: source_dir,
89
- target_dir: @workspace.xhtml_dir,
90
- verbose: verbose
91
- }).run
92
- end
93
-
94
- def split_xhtmls
95
- log 'Splitting XHTML files into chapters...'
96
- Dir.glob(File.join(@workspace.xhtml_dir, '*.xhtml')).each do |xhtml_file|
97
- split_xhtml_file(xhtml_file)
98
- end
99
- end
23
+ def output_path = @output_file
100
24
 
101
- def split_xhtml_file(xhtml_file)
102
- base = File.basename(xhtml_file, '.xhtml')
103
- log "Splitting '#{base}'..."
104
- SplitChapters.new(build_split_options(xhtml_file)).run
105
- end
106
-
107
- def build_split_options(xhtml_file)
108
- {
109
- input_file: xhtml_file,
110
- book_title: title,
111
- output_dir: @workspace.chapters_dir,
112
- output_prefix: 'chapter',
113
- verbose: verbose
114
- }
115
- end
116
-
117
- def validate_chapters
118
- ChapterValidator.new(chapters_dir: @workspace.chapters_dir, verbose: verbose).validate
119
- end
120
-
121
- def initialize_epub
25
+ def before_add_chapters
122
26
  log 'Initializing new EPUB...'
123
- EpubInitializer.new(build_epub_options).run
124
- end
125
-
126
- def build_epub_options
127
- options = { title: title, author: author, destination: @workspace.epub_dir }
27
+ options = { title: title, author: author, destination: epub_dir }
128
28
  options[:cover_image] = cover_image if cover_image
129
- options
130
- end
131
-
132
- def add_chapters
133
- log 'Adding chapters to EPUB...'
134
- AddChapters.new({
135
- chapters_dir: @workspace.chapters_dir,
136
- epub_dir: File.join(@workspace.epub_dir, 'OEBPS'),
137
- verbose: verbose
138
- }).run
29
+ EpubInitializer.new(options).run
139
30
  end
140
31
 
141
- def pack_epub
142
- log "Building final EPUB '#{output_file}'..."
143
- PackEbook.new({
144
- input_dir: @workspace.epub_dir,
145
- output_file: output_file,
146
- verbose: verbose
147
- }).run
32
+ def default_output_file
33
+ "#{title.gsub(' ', '_')}.epub"
148
34
  end
149
35
  end
150
36
  end