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 +4 -4
- data/.ruby-version +1 -1
- data/Brewfile +1 -0
- data/CHANGELOG.md +33 -0
- data/README.md +75 -9
- data/Rakefile +4 -0
- data/SYSTEM.md +2 -2
- data/lib/comic_book/adapter.rb +4 -0
- data/lib/comic_book/cb/archiver.rb +49 -0
- data/lib/comic_book/cb/extractor.rb +17 -0
- data/lib/comic_book/cb.rb +36 -0
- data/lib/comic_book/cb7/archiver.rb +6 -6
- data/lib/comic_book/cb7/extractor.rb +36 -21
- data/lib/comic_book/cb7.rb +27 -13
- data/lib/comic_book/cba.rb +21 -0
- data/lib/comic_book/cbr/extractor.rb +75 -0
- data/lib/comic_book/cbr.rb +50 -0
- data/lib/comic_book/cbt/archiver.rb +22 -31
- data/lib/comic_book/cbt/extractor.rb +38 -41
- data/lib/comic_book/cbt.rb +30 -12
- data/lib/comic_book/cbz/archiver.rb +6 -6
- data/lib/comic_book/cbz/extractor.rb +30 -19
- data/lib/comic_book/cbz.rb +22 -12
- data/lib/comic_book/cli.rb +183 -26
- data/lib/comic_book/cli_helpers.rb +37 -10
- data/lib/comic_book/pdf/extractor.rb +75 -0
- data/lib/comic_book/pdf.rb +45 -0
- data/lib/comic_book/vendor/macos/lsar +0 -0
- data/lib/comic_book/vendor/macos/unar +0 -0
- data/lib/comic_book/version.rb +1 -1
- data/lib/comicbook.rb +30 -5
- metadata +46 -7
- data/lib/comic_book/vendor/linux/lsar +0 -0
- data/lib/comic_book/vendor/linux/unar +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8e9032e12765aa91a64364150391122a0324ee9f89b9987391f4708d1db7b0d6
|
|
4
|
+
data.tar.gz: de1dc67b4d78eb8ecf739b02381d78cf2a39518a6b12dd6bbef521dd77e1b8dd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 81a45ca97a476926a1d8216043069840e27f42c461175ebf0326cd0698e4cc0ea78f8627b3b932e23266ccc56081786eaec210cbbc07d1e5e452b11bcef94f4e
|
|
7
|
+
data.tar.gz: f073563d4dcc6253d140e6818f2c9e273dfdc8e6ce053a729b6cddddb115ef436d6afa80d3e4357cf2ff187112bb69f444032674a5a96db841a7df0664ff0f7e
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
40
|
+
### CLI
|
|
37
41
|
|
|
38
|
-
|
|
42
|
+
```sh
|
|
43
|
+
# Extract a comic book archive
|
|
44
|
+
comicbook extract path/to/archive.cbz
|
|
39
45
|
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
78
|
+
|
|
79
|
+
### Ruby API
|
|
44
80
|
|
|
45
81
|
```ruby
|
|
46
|
-
|
|
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
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.
|
|
234
|
-
- **Ruby Requirement**: >=
|
|
233
|
+
- **Current Version**: 0.2.0
|
|
234
|
+
- **Ruby Requirement**: >= 4.0.0
|
|
235
235
|
- **License**: MIT
|
data/lib/comic_book/adapter.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
|
30
|
+
def create_archive output_path
|
|
31
31
|
File.open(output_path, 'wb') do |file|
|
|
32
|
-
SevenZipRuby::Writer.open(file) do |
|
|
32
|
+
SevenZipRuby::Writer.open(file) do |writer|
|
|
33
33
|
find_image_files.each do |image_file|
|
|
34
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
5
|
+
@archive_path = File.expand_path archive_path
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def extract options = {}
|
|
9
|
-
extension
|
|
10
|
-
delete_original
|
|
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
|
-
|
|
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
|
|
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
|
|
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 |
|
|
40
|
-
|
|
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
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
68
|
+
extract_single_file entry, destination, seven_zip_reader
|
|
69
|
+
end
|
|
54
70
|
end
|
|
55
71
|
|
|
56
|
-
def
|
|
57
|
-
File.
|
|
58
|
-
|
|
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
|
-
|
|
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
|
data/lib/comic_book/cb7.rb
CHANGED
|
@@ -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
|
|
17
|
+
def info
|
|
18
|
+
xml = nil
|
|
17
19
|
|
|
18
|
-
|
|
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
|
-
|
|
21
|
-
pages = []
|
|
27
|
+
return nil unless xml
|
|
22
28
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
ComicInfo.load xml
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def pages
|
|
33
|
+
entries = []
|
|
27
34
|
|
|
28
|
-
|
|
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
|
-
|
|
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
|
|
51
|
+
basename = File.basename entry
|
|
38
52
|
|
|
39
|
-
ComicBook::Page.new entry
|
|
53
|
+
ComicBook::Page.new entry, basename
|
|
40
54
|
end
|
|
41
55
|
|
|
42
56
|
def image_file? filename
|
|
43
|
-
extension = File.extname
|
|
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
|