comicbook 0.1.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a990cb3f4650a5a8efb0de5f872f7242c6a3e9f7028f137022d003f92f401f4b
4
- data.tar.gz: b10b662cfd46bbe3126b4c834b2e5c057a227d18bed5d0a97d4bc899af843192
3
+ metadata.gz: 8e9032e12765aa91a64364150391122a0324ee9f89b9987391f4708d1db7b0d6
4
+ data.tar.gz: de1dc67b4d78eb8ecf739b02381d78cf2a39518a6b12dd6bbef521dd77e1b8dd
5
5
  SHA512:
6
- metadata.gz: 4d7e88350b4c8f9c111d68579509bdc90f8dbee09abf326a5255b9f2c4065255c1d6d863ff57df3cb42279565aee09ed67873bce779f3d734537b57c03745330
7
- data.tar.gz: '0059293bd6b56943810b2fd5bb7ed730dfc1ad214371a5467e2ab73cd7fd8604b4ca66b822ca74051beb10187c15d193d589f58b6bfa02e7ed0d64d81cb3571c'
6
+ metadata.gz: 81a45ca97a476926a1d8216043069840e27f42c461175ebf0326cd0698e4cc0ea78f8627b3b932e23266ccc56081786eaec210cbbc07d1e5e452b11bcef94f4e
7
+ data.tar.gz: f073563d4dcc6253d140e6818f2c9e273dfdc8e6ce053a729b6cddddb115ef436d6afa80d3e4357cf2ff187112bb69f444032674a5a96db841a7df0664ff0f7e
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.4.7
1
+ 4.0.2
data/Brewfile ADDED
@@ -0,0 +1 @@
1
+ brew 'vips'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2026-04-14
4
+
5
+ ### Added
6
+
7
+ - PDF to comic book conversion (PDF → .cb → .cbz/.cb7/.cbt) with configurable DPI (default 300)
8
+ - PDF adapter with extract and pages support using libvips
9
+ - `ruby-vips` gem dependency (lazy-loaded, only required for PDF features)
10
+ - `ComicBook#info` method to read ComicInfo.xml metadata from archives and folders
11
+ - `comicinfo` gem dependency for ComicInfo.xml parsing
12
+ - `ComicBook::Info` alias for `ComicInfo::Issue`
13
+ - `comicbook info` CLI subcommand with `--format` (verbose, terse, json, yaml), `--only`, and `--except` options
14
+ - `--dpi` CLI option for PDF extraction
15
+ - `--version`/`-v` CLI flag
16
+ - Install `libvips-dev` in CI workflow
17
+
18
+ ## [0.2.0] - 2025-11-17
19
+
20
+ ### Added
21
+
22
+ - CBR extraction support using vendored `unar`/`lsar` binaries
23
+ - `--delete-original` CLI option for both extract and archive commands
24
+ - `--images-only` CLI option for extract command
25
+ - `images_only` option for Ruby API extraction
26
+ - `delete_original` option for Ruby API extraction and archiving
27
+ - CLI validation for unsupported archive output formats (CBR, CBA)
28
+
3
29
  ## [0.1.0] - 2025-10-26
4
30
 
31
+ ### Added
32
+
5
33
  - Initial release
34
+ - CBZ support (extract and archive)
35
+ - CB7 support (extract and archive)
36
+ - CBT support (extract and archive)
37
+ - CLI tool with `extract` and `archive` commands
38
+ - Ruby API with `ComicBook.extract`, `ComicBook.new().archive`, and `ComicBook.new().pages`
data/README.md CHANGED
@@ -7,14 +7,18 @@ A Ruby library and CLI tool for managing comic books archives.
7
7
 
8
8
  **`archive`** — to create a `.cb*` file (default: `.cbz`).
9
9
 
10
- Currently supported formats, `archive` and `extract`:
10
+ **`info`** to read ComicInfo.xml metadata from a `.cb*` file.
11
+
12
+ Currently supported formats for `archive` and `extract`:
11
13
  - CB7 — [7zip](https://en.wikipedia.org/wiki/7-Zip)
12
14
  - CBT — [Tar](https://en.wikipedia.org/wiki/Tar_(computing))
13
15
  - CBZ — [Zip](https://en.wikipedia.org/wiki/ZIP_(file_format))
14
16
 
15
- Planned formats , only `extract`:
17
+ Currently supported formats for `extract` only:
18
+ - **CBR** — [RAR](https://en.wikipedia.org/wiki/WinRAR) is proprietary without an open source implementation license. Extracting support is provided using vendored [`unar`](https://theunarchiver.com/command-line) binaries because a large number of comic books are archived in .cbr/.rar format. No support for creating `.cbr` files will ever be added until RAR is open source (or reverse engineered).
19
+ - **PDF** — Extract PDF pages as images using [libvips](https://www.libvips.org/). Requires libvips installed on the system.
16
20
 
17
- - **CBR** [RAR](https://en.wikipedia.org/wiki/WinRAR) is proprietary without an open source implementation license. People use WinRAR (Windows-only) to create .rar files. Or `unrar` on Linux/macOS to open .rar files. Extracting support is provided because a large number of comic books are archived in .cbr/.rar format, primarily by Windows users. No support for creating `.cbr` files will ever be added until RAR is opensource (or reverse engineered).
21
+ Planned formats for `extract` only:
18
22
  - **CBA** — [ACE](https://en.wikipedia.org/wiki/WinAce) is both proprietary and very old/outdated/unsupported. ACE extracting support is provided for historical posterity and completeness.
19
23
 
20
24
  ## Installation
@@ -33,21 +37,83 @@ gem install comicbook
33
37
 
34
38
  ## Usage
35
39
 
36
- In Ruby, you can use the `ComicBook` class to `extract` comic books archives from various formats. You `archive` a folder of images to create a comic book archive.
40
+ ### CLI
37
41
 
38
- ### Extracting
42
+ ```sh
43
+ # Extract a comic book archive
44
+ comicbook extract path/to/archive.cbz
39
45
 
40
- ```ruby
41
- ComicBook.extract 'path/to/archive.cbz'
46
+ # Extract to a specific destination
47
+ comicbook extract path/to/archive.cbz --to path/to/output
48
+
49
+ # Extract only image files (exclude metadata like ComicInfo.xml)
50
+ comicbook extract path/to/archive.cbz --images-only
51
+
52
+ # Extract PDF at custom DPI (default: 300)
53
+ comicbook extract path/to/comic.pdf --dpi 600
54
+
55
+ # Create a comic book archive from a folder
56
+ comicbook archive path/to/folder
57
+
58
+ # Create archive at a specific destination
59
+ comicbook archive path/to/folder --to path/to/output.cbz
60
+
61
+ # Show ComicInfo.xml metadata
62
+ comicbook info path/to/archive.cbz
63
+
64
+ # Show info in different formats
65
+ comicbook info path/to/archive.cbz --format json
66
+ comicbook info path/to/archive.cbz --format yaml
67
+ comicbook info path/to/archive.cbz --format terse
68
+
69
+ # Show only specific fields
70
+ comicbook info path/to/archive.cbz --only title,writer,publisher
71
+
72
+ # Show all fields except specific ones
73
+ comicbook info path/to/archive.cbz --except summary,review
74
+
75
+ # Show help
76
+ comicbook --help
42
77
  ```
43
- ### Archiving
78
+
79
+ ### Ruby API
44
80
 
45
81
  ```ruby
46
- ComicBook.archive 'path/to/archive'
82
+ # Extract a comic book archive (extracts all files by default)
83
+ ComicBook.extract 'path/to/archive.cbz'
84
+
85
+ # Extract to a specific destination
86
+ ComicBook.extract 'path/to/archive.cbz', to: 'path/to/output'
87
+
88
+ # Extract only image files (exclude metadata like ComicInfo.xml)
89
+ ComicBook.extract 'path/to/archive.cbz', images_only: true
90
+
91
+ # Create a comic book archive from a folder (creates .cbz by default)
92
+ ComicBook.new('path/to/folder').archive
93
+
94
+ # Create archive at a specific destination
95
+ ComicBook.new('path/to/folder').archive to: 'path/to/output.cbz'
96
+
97
+ # Get pages from an archive
98
+ comic = ComicBook.new 'path/to/archive.cbz'
99
+ comic.pages # => [#<ComicBook::Page>, ...]
100
+
101
+ # Read ComicInfo.xml metadata
102
+ comic = ComicBook.new 'path/to/archive.cbz'
103
+ comic.info # => #<ComicInfo::Issue> or nil
104
+ comic.info.title # => "The Amazing Spider-Man"
105
+ comic.info.writer # => "Dan Slott, Christos Gage"
47
106
  ```
48
107
 
49
108
  ## Development
50
109
 
110
+ PDF support requires [libvips](https://www.libvips.org/). Install it with `brew bundle` (uses the included `Brewfile`) or manually:
111
+
112
+ ```sh
113
+ brew install vips # macOS
114
+ sudo apt install libvips-dev # Linux
115
+ ```
116
+
51
117
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
52
118
 
53
119
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
data/Rakefile CHANGED
@@ -5,4 +5,8 @@ require 'rubocop/rake_task'
5
5
  RSpec::Core::RakeTask.new :spec
6
6
  RuboCop::RakeTask.new
7
7
 
8
+ desc 'Run tests and linter'
8
9
  task default: %i[spec rubocop]
10
+
11
+ desc 'Alias of default: spec linter'
12
+ task test: %i[spec rubocop]
data/SYSTEM.md CHANGED
@@ -230,6 +230,6 @@ ComicBook::Error < StandardError
230
230
  The adapter pattern makes it easy to add new formats or extend existing ones without modifying core functionality.
231
231
 
232
232
  ## Version Information
233
- - **Current Version**: 0.1.0
234
- - **Ruby Requirement**: >= 3.4.7
233
+ - **Current Version**: 0.2.0
234
+ - **Ruby Requirement**: >= 4.0.0
235
235
  - **License**: MIT
@@ -19,6 +19,10 @@ class ComicBook
19
19
  raise NotImplementedError, "#{self.class} must implement #pages"
20
20
  end
21
21
 
22
+ def info
23
+ raise NotImplementedError, "#{self.class} must implement #info"
24
+ end
25
+
22
26
  private
23
27
 
24
28
  attr_reader :path
@@ -0,0 +1,49 @@
1
+ class ComicBook
2
+ class CB < Adapter
3
+ class Archiver
4
+ def initialize source_path
5
+ @source_path = File.expand_path source_path
6
+ end
7
+
8
+ def archive options = {}
9
+ output_path = options[:to] || determine_output_path
10
+
11
+ validate_destination! output_path
12
+
13
+ if File.directory? source_path
14
+ archive_folder output_path
15
+ else
16
+ archive_file output_path
17
+ end
18
+
19
+ output_path
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :source_path
25
+
26
+ def determine_output_path
27
+ base_name = File.basename source_path, '.*'
28
+ dir_name = File.dirname source_path
29
+
30
+ File.expand_path File.join(dir_name, "#{base_name}.cb")
31
+ end
32
+
33
+ def validate_destination! output_path
34
+ return unless File.exist? output_path
35
+
36
+ raise ComicBook::Error, "Destination already exists: #{output_path}"
37
+ end
38
+
39
+ def archive_folder output_path
40
+ FileUtils.mv source_path, output_path
41
+ end
42
+
43
+ def archive_file output_path
44
+ FileUtils.mkdir_p output_path
45
+ FileUtils.mv source_path, output_path
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,17 @@
1
+ class ComicBook
2
+ class CB < Adapter
3
+ class Extractor
4
+ def initialize archive_path
5
+ @archive_path = File.expand_path archive_path
6
+ end
7
+
8
+ def extract
9
+ raise ComicBook::Error, '.cb folders are already extracted (they are uncompressed folders)'
10
+ end
11
+
12
+ private
13
+
14
+ attr_reader :archive_path
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,36 @@
1
+ require 'comicinfo'
2
+ require_relative 'adapter'
3
+ require_relative 'cb/archiver'
4
+ require_relative 'cb/extractor'
5
+
6
+ class ComicBook
7
+ class CB < Adapter
8
+ def archive options = {}
9
+ Archiver.new(path).archive options
10
+ end
11
+
12
+ def info
13
+ xml_path = File.join path, 'ComicInfo.xml'
14
+ return nil unless File.exist? xml_path
15
+
16
+ ComicInfo.load xml_path
17
+ end
18
+
19
+ def extract _options = {}
20
+ Extractor.new(path).extract
21
+ end
22
+
23
+ def pages
24
+ pattern = ComicBook::IMAGE_GLOB_PATTERN
25
+ search_path = File.join path, '**', pattern
26
+ image_files = Dir.glob search_path, File::FNM_CASEFOLD
27
+
28
+ image_files.sort.map do |file|
29
+ relative_path = Pathname.new(file).relative_path_from(Pathname.new(path)).to_s
30
+ basename = File.basename file
31
+
32
+ ComicBook::Page.new relative_path, basename
33
+ end
34
+ end
35
+ end
36
+ end
@@ -10,7 +10,7 @@ class ComicBook
10
10
  delete_original = options.fetch :delete_original, false
11
11
 
12
12
  output_path = options[:to] || determine_output_path(extension)
13
- create_7z_archive output_path
13
+ create_archive output_path
14
14
  cleanup_source_folder if delete_original
15
15
 
16
16
  output_path
@@ -27,11 +27,11 @@ class ComicBook
27
27
  File.expand_path File.join(dir_name, "#{base_name}.#{extension}")
28
28
  end
29
29
 
30
- def create_7z_archive output_path
30
+ def create_archive output_path
31
31
  File.open(output_path, 'wb') do |file|
32
- SevenZipRuby::Writer.open(file) do |szw|
32
+ SevenZipRuby::Writer.open(file) do |writer|
33
33
  find_image_files.each do |image_file|
34
- add_file_to_7z szw, image_file
34
+ add_file writer, image_file
35
35
  end
36
36
  end
37
37
  end
@@ -42,12 +42,12 @@ class ComicBook
42
42
  Dir.glob(pattern, File::FNM_CASEFOLD).sort
43
43
  end
44
44
 
45
- def add_file_to_7z szw, file
45
+ def add_file writer, file
46
46
  file_path = Pathname.new file
47
47
  source_path = Pathname.new source_folder
48
48
  relative_path = file_path.relative_path_from source_path
49
49
 
50
- szw.add_file file, as: relative_path.to_s
50
+ writer.add_file file, as: relative_path.to_s
51
51
  end
52
52
 
53
53
  def cleanup_source_folder
@@ -2,16 +2,17 @@ class ComicBook
2
2
  class CB7 < Adapter
3
3
  class Extractor
4
4
  def initialize archive_path
5
- @archive_path = File.expand_path(archive_path)
5
+ @archive_path = File.expand_path archive_path
6
6
  end
7
7
 
8
8
  def extract options = {}
9
- extension = options.fetch :extension, :cb
10
- delete_original = options.fetch :delete_original, false
9
+ extension = options.fetch :extension, :cb
10
+ delete_original = options.fetch :delete_original, false
11
11
  destination_folder = options[:to]
12
12
 
13
13
  destination = destination_folder || determine_extract_path(extension)
14
- extract_7z_contents destination
14
+ create_destination_directory destination
15
+ extract_contents destination, options
15
16
  cleanup_archive_file if delete_original
16
17
 
17
18
  destination
@@ -21,9 +22,13 @@ class ComicBook
21
22
 
22
23
  attr_reader :archive_path
23
24
 
25
+ def create_destination_directory destination
26
+ FileUtils.mkdir_p destination
27
+ end
28
+
24
29
  def determine_extract_path extension
25
30
  base_name = File.basename archive_path, '.*'
26
- dir_name = File.dirname archive_path
31
+ dir_name = File.dirname archive_path
27
32
  archive_name = base_name
28
33
 
29
34
  archive_name << ".#{extension}" if extension
@@ -32,33 +37,43 @@ class ComicBook
32
37
  File.expand_path full_path
33
38
  end
34
39
 
35
- def extract_7z_contents destination
40
+ def image_file? filename
41
+ ComicBook::IMAGE_EXTENSIONS.include? File.extname(filename.downcase)
42
+ end
43
+
44
+ def cleanup_archive_file
45
+ File.delete archive_path
46
+ end
47
+
48
+ def create_parent_directory file_path
49
+ parent_dir = File.dirname file_path
50
+ FileUtils.mkdir_p parent_dir
51
+ end
52
+
53
+ def extract_contents destination, options
36
54
  FileUtils.mkdir_p destination
37
55
 
38
56
  File.open(archive_path, 'rb') do |file|
39
- SevenZipRuby::Reader.open(file) do |szr|
40
- szr.entries.each do |entry|
41
- next unless entry.file? && image_file?(entry.path)
42
-
43
- extract_single_file entry, destination, szr
44
- end
57
+ SevenZipRuby::Reader.open(file) do |seven_zip_reader|
58
+ extract_files destination, options, seven_zip_reader
45
59
  end
46
60
  end
47
61
  end
48
62
 
49
- def extract_single_file entry, destination, szr
50
- file_path = File.join(destination, entry.path)
51
- FileUtils.mkdir_p File.dirname(file_path)
63
+ def extract_files destination, options, seven_zip_reader
64
+ seven_zip_reader.entries.each do |entry|
65
+ next unless entry.file?
66
+ next if options[:images_only] && !image_file?(entry.path)
52
67
 
53
- File.binwrite(file_path, szr.extract_data(entry))
68
+ extract_single_file entry, destination, seven_zip_reader
69
+ end
54
70
  end
55
71
 
56
- def cleanup_archive_file
57
- File.delete archive_path
58
- end
72
+ def extract_single_file entry, destination, seven_zip_reader
73
+ file_path = File.join destination, entry.path
74
+ create_parent_directory file_path
59
75
 
60
- def image_file? filename
61
- ComicBook::IMAGE_EXTENSIONS.include? File.extname(filename.downcase)
76
+ File.binwrite(file_path, seven_zip_reader.extract_data(entry))
62
77
  end
63
78
  end
64
79
  end
@@ -1,4 +1,5 @@
1
1
  require 'seven_zip_ruby'
2
+ require 'comicinfo'
2
3
  require_relative 'adapter'
3
4
  require_relative 'cb7/archiver'
4
5
  require_relative 'cb7/extractor'
@@ -13,34 +14,47 @@ class ComicBook
13
14
  Extractor.new(path).extract options
14
15
  end
15
16
 
16
- def pages = collect_pages_from_7z
17
+ def info
18
+ xml = nil
17
19
 
18
- private
20
+ File.open(path, 'rb') do |file|
21
+ SevenZipRuby::Reader.open(file) do |reader|
22
+ entry = reader.entries.find { it.path == 'ComicInfo.xml' }
23
+ xml = reader.extract_data(entry.index) if entry
24
+ end
25
+ end
19
26
 
20
- def collect_pages_from_7z
21
- pages = []
27
+ return nil unless xml
22
28
 
23
- File.open(path, 'rb') do |file|
24
- SevenZipRuby::Reader.open(file) do |szr|
25
- szr.entries.each do |entry|
26
- next unless entry.file? && image_file?(entry.path)
29
+ ComicInfo.load xml
30
+ end
31
+
32
+ def pages
33
+ entries = []
27
34
 
28
- pages << create_page_from_entry(entry)
35
+ File.open(path, 'rb') do |file|
36
+ SevenZipRuby::Reader.open(file) do |reader|
37
+ reader.entries.each do |entry|
38
+ entries << entry.path if entry.file?
29
39
  end
30
40
  end
31
41
  end
32
42
 
33
- pages.sort_by(&:name)
43
+ entries.select { image_file? it }
44
+ .map { create_page_from_entry it }
45
+ .sort_by(&:name)
34
46
  end
35
47
 
48
+ private
49
+
36
50
  def create_page_from_entry entry
37
- basename = File.basename(entry.path)
51
+ basename = File.basename entry
38
52
 
39
- ComicBook::Page.new entry.path, basename
53
+ ComicBook::Page.new entry, basename
40
54
  end
41
55
 
42
56
  def image_file? filename
43
- extension = File.extname(filename.downcase)
57
+ extension = File.extname filename.downcase
44
58
 
45
59
  ComicBook::IMAGE_EXTENSIONS.include? extension
46
60
  end
@@ -0,0 +1,21 @@
1
+ require_relative 'adapter'
2
+
3
+ class ComicBook
4
+ class CBA < Adapter
5
+ def archive _options = {}
6
+ raise Error, 'CBA archiving not supported (ACE is proprietary)'
7
+ end
8
+
9
+ def extract _options = {}
10
+ raise Error, 'CBA extraction not yet implemented'
11
+ end
12
+
13
+ def info
14
+ raise Error, 'CBA info not yet implemented'
15
+ end
16
+
17
+ def pages
18
+ raise Error, 'CBA page listing not yet implemented'
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,75 @@
1
+ class ComicBook
2
+ class CBR < Adapter
3
+ class Extractor
4
+ def initialize archive_path
5
+ @archive_path = File.expand_path archive_path
6
+ end
7
+
8
+ def extract options = {}
9
+ extension = options.fetch :extension, :cb
10
+ delete_original = options.fetch :delete_original, false
11
+ destination_folder = options[:to]
12
+
13
+ destination = destination_folder || determine_extract_path(extension)
14
+ create_destination_directory destination
15
+ extract_contents destination, options
16
+ cleanup_archive_file if delete_original
17
+
18
+ destination
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :archive_path
24
+
25
+ def create_destination_directory destination
26
+ FileUtils.mkdir_p destination
27
+ end
28
+
29
+ def determine_extract_path extension
30
+ base_name = File.basename archive_path, '.*'
31
+ dir_name = File.dirname archive_path
32
+ archive_name = base_name
33
+
34
+ archive_name << ".#{extension}" if extension
35
+
36
+ full_path = File.join dir_name, archive_name
37
+ File.expand_path full_path
38
+ end
39
+
40
+ def image_file? filename
41
+ ComicBook::IMAGE_EXTENSIONS.include? File.extname(filename.downcase)
42
+ end
43
+
44
+ def cleanup_archive_file
45
+ File.delete archive_path
46
+ end
47
+
48
+ def create_parent_directory file_path
49
+ parent_dir = File.dirname file_path
50
+ FileUtils.mkdir_p parent_dir
51
+ end
52
+
53
+ def extract_contents destination, options
54
+ FileUtils.mkdir_p destination
55
+ extract_files destination, options
56
+ end
57
+
58
+ def extract_files destination, options
59
+ CLIHelpers.unar_extract archive_path, destination
60
+ delete_non_images destination if options[:images_only]
61
+ end
62
+
63
+ def delete_non_images destination
64
+ archive_entries = CLIHelpers.lsar_list archive_path
65
+
66
+ archive_entries.each do |entry|
67
+ next if image_file? entry
68
+
69
+ file_path = File.join(destination, entry)
70
+ FileUtils.rm_f file_path
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,50 @@
1
+ require 'shellwords'
2
+ require 'comicinfo'
3
+ require_relative 'adapter'
4
+ require_relative 'cbr/extractor'
5
+
6
+ class ComicBook
7
+ class CBR < Adapter
8
+ def archive _options = {}
9
+ raise Error, 'CBR archiving not supported (RAR is proprietary)'
10
+ end
11
+
12
+ def extract options = {}
13
+ Extractor.new(path).extract options
14
+ end
15
+
16
+ def info
17
+ entries = CLIHelpers.lsar_list path
18
+ return nil unless entries.include? 'ComicInfo.xml'
19
+
20
+ Dir.mktmpdir do |temp_dir|
21
+ CLIHelpers.unar_extract path, temp_dir
22
+ xml_path = File.join temp_dir, 'ComicInfo.xml'
23
+ return nil unless File.exist? xml_path
24
+
25
+ ComicInfo.load xml_path
26
+ end
27
+ end
28
+
29
+ def pages
30
+ CLIHelpers.lsar_list(path)
31
+ .select { image_file? it }
32
+ .map { create_page_from_entry it }
33
+ .sort_by(&:name)
34
+ end
35
+
36
+ private
37
+
38
+ def create_page_from_entry entry
39
+ basename = File.basename entry
40
+
41
+ ComicBook::Page.new entry, basename
42
+ end
43
+
44
+ def image_file? filename
45
+ extension = File.extname filename.downcase
46
+
47
+ ComicBook::IMAGE_EXTENSIONS.include? extension
48
+ end
49
+ end
50
+ end