cbz_tools 0.4.0 → 0.5.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: 96ee68c0b79bcb1256324ad692bf441f0926443ab71e87db3715c0ca5c89cac0
4
- data.tar.gz: 15d6235b0d040d8836a752da8e304bb89d2fd0976f9eb371f6f514b6dd8c97c4
3
+ metadata.gz: 30f58af31425d78057d97105cfe21164b6ec05263f0b86d2b2a0dca150792d5d
4
+ data.tar.gz: edd397b9588670cd49a3bcfe517f684ae9f684f4469a9725d1ac440f00da0fbe
5
5
  SHA512:
6
- metadata.gz: dfaee61a3965d6901835d58ca0206ee5bd5f4a05eb1ffb00d2248ca16755aeb1d418b54f3550fe9ef6d0d125aff393955aec459990a279259bc05d874e7d1adc
7
- data.tar.gz: 7e2e12a38206a9a6ea43d49eb4474a60770d094c48d47746a3cb6f249496abf382af564caacd41e5426684077a23135018ef108795232c87e592a835740ef435
6
+ metadata.gz: 57e09be8a89fec63e2cb22b140c6500afa29a0363a1f0e7b1b6d5ef75b9a137e4f6da4b1012031d18b2875db856e73145ded2fabf8532a6080d9f80d9ff98894
7
+ data.tar.gz: 73deffc4ac234b2288b2b8c5898cd12dc005561b79a7b5941cfb052ace8766fef6227bf935b6835226381f679d0bfb5fd2f9553fc68fd8cb2bc5ce309e7bfd45
@@ -1,3 +1,3 @@
1
1
  {
2
- "cSpell.words": ["comicinfo", "gtin", "penciller"]
2
+ "cSpell.words": ["comicinfo", "gtin", "penciller", "unrar"]
3
3
  }
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.0] - 2026-08-04
4
+
5
+ - `CbzTools::CbrConverter` now uses `unrar` directly — no extractor param required.
6
+ - **Breaking**: Removed `extractor:` param, `ExtractionError`, and `extractor` attribute.
7
+ - Runtime check raises `ArchiveError` if `unrar` is not in PATH.
8
+
3
9
  ## [0.4.0] - 2026-08-03
4
10
 
5
11
  - Add `CbzTools::CbrConverter` for converting CBR archives to CBZ. The gem ships the orchestration (extract → repack → error model) but no default extractor — callers pass `extractor:` (anything responding to `call(cbr_path, destination_dir)` and raising `ExtractionError` on failure).
data/README.md CHANGED
@@ -38,37 +38,17 @@ end
38
38
 
39
39
  ### Converting CBR to CBZ
40
40
 
41
- `CbzTools::CbrConverter` orchestrates the extract repack CBZ step,
42
- but it does **not** ship an extractor. Pass anything responding to
43
- `call(cbr_path, destination_dir)` (and that raises `ExtractionError`
44
- on failure):
41
+ `CbzTools::CbrConverter` converts CBR archives to CBZ using the `unrar`
42
+ command-line tool (which must be installed on your system).
45
43
 
46
44
  ```ruby
47
- extractor = MyApp::UnrarExtractor.new
48
- CbzTools::CbrConverter.convert("path/to/comic.cbr",
49
- to: "path/to/comic.cbz",
50
- extractor: extractor)
51
- ```
52
-
53
- Reuse one extractor across many conversions with `work_in:` to pool
54
- the tmpdir:
55
-
56
- ```ruby
57
- extractor = MyApp::UnrarExtractor.new
58
- work_in = Dir.mktmpdir("batch")
59
- converter = CbzTools::CbrConverter.new(extractor: extractor)
60
-
61
- paths.each do |cbr|
62
- converter.convert(cbr, to: cbr.sub(/\.cbr\z/i, ".cbz"), work_in: work_in)
63
- end
45
+ CbzTools::CbrConverter.convert("path/to/comic.cbr", to: "path/to/comic.cbz")
64
46
  ```
65
47
 
66
48
  Errors inherit from `CbzTools::Error`:
67
49
 
68
- - `CbzTools::CbrConverter::ExtractionError` — the extractor failed.
69
- - `CbzTools::CbrConverter::ArchiveError` no extractor supplied,
70
- the destination couldn't be written, or `overwrite: false` and the
71
- destination already exists.
50
+ - `CbzTools::CbrConverter::ArchiveError` — the destination couldn't be written,
51
+ `overwrite: false` and the destination already exists, or `unrar` is not installed.
72
52
 
73
53
  ## Development
74
54
 
@@ -4,45 +4,28 @@ require "tmpdir"
4
4
  require "fileutils"
5
5
  require "zip"
6
6
  require "pathname"
7
+ require "open3"
8
+
9
+ require_relative "system_dependencies"
7
10
 
8
11
  module CbzTools
9
12
  # Converts a CBR (Comic Book RAR) archive into a CBZ (Comic Book ZIP)
10
13
  # archive.
11
14
  #
12
- # The CBR extraction step is delegated to an injectable +extractor+
13
- # anything that responds to +call(cbr_path, destination_dir)+ and
14
- # raises {CbzTools::CbrConverter::ExtractionError} on failure. Callers (not this gem) own the
15
- # extractor; this class is deliberately agnostic about which binary,
16
- # library, or pure-Ruby implementation is used to expand the source.
15
+ # Extraction is handled internally using the +unrar+ command-line tool,
16
+ # which must be installed on the system. Consumers do not need to
17
+ # install or configure any extraction backend.
17
18
  #
18
- # @example Single-shot conversion with an explicit extractor
19
- # extractor = MyApp::UnrarExtractor.new
20
- # CbzTools::CbrConverter.convert("path/to/comic.cbr",
21
- # to: "path/to/comic.cbz",
22
- # extractor: extractor)
19
+ # @example Single-shot conversion
20
+ # CbzTools::CbrConverter.convert("path/to/comic.cbr", to: "path/to/comic.cbz")
23
21
  #
24
- # @example Reusing one extractor across many conversions
25
- # extractor = MyApp::UnrarExtractor.new
26
- # converter = CbzTools::CbrConverter.new(extractor: extractor)
22
+ # @example Reusing one converter across many conversions
23
+ # converter = CbzTools::CbrConverter.new
27
24
  # paths.each { |p| converter.convert(p, to: p.sub(/\.cbr\z/i, ".cbz")) }
28
25
  class CbrConverter
29
- # Raised when the underlying extractor fails to expand the source
30
- # CBR into a directory. Propagated untouched so callers can rescue
31
- # it without unwrapping.
32
- class ExtractionError < Error
33
- end
34
-
35
- # Raised when the resulting CBZ cannot be written, when the
36
- # destination already exists and +overwrite:+ is false, or when no
37
- # +extractor:+ was supplied. Propagated untouched from the
38
- # relevant step.
39
26
  class ArchiveError < Error
40
27
  end
41
28
 
42
- # @return [#call, nil] the configured extractor, or +nil+ if none
43
- # was supplied. Read-only; there is no corresponding writer.
44
- attr_reader :extractor
45
-
46
29
  # @see #convert
47
30
  #
48
31
  # @param cbr_path [String] source CBR path
@@ -53,39 +36,18 @@ module CbzTools
53
36
  # a fresh +Dir.mktmpdir+ is created and cleaned up. When set, the
54
37
  # converter uses that directory and leaves cleanup to the caller
55
38
  # (useful when batching many conversions).
56
- # @param extractor [#call] extractor backend; required.
57
39
  # @return [String] the destination CBZ path
58
- # @raise [ArchiveError] when +extractor:+ is missing or target cannot be written
59
- # @raise [ExtractionError] when the extractor fails
60
- def self.convert(cbr_path, to:, overwrite: true, work_in: nil, extractor: nil)
61
- new(extractor: extractor).convert(cbr_path, to: to, overwrite: overwrite, work_in: work_in)
40
+ # @raise [ArchiveError] when target cannot be written or unrar is unavailable
41
+ def self.convert(cbr_path, to:, overwrite: true, work_in: nil)
42
+ new.convert(cbr_path, to: to, overwrite: overwrite, work_in: work_in)
62
43
  end
63
44
 
64
- # @param extractor [#call, nil] anything responding to
65
- # +call(cbr_path, destination_dir)+ that raises {ExtractionError}
66
- # on failure. Required — {#convert} raises {ArchiveError} if left
67
- # as +nil+.
68
- def initialize(extractor: nil)
69
- @extractor = extractor
70
- end
71
-
72
- # @see .convert
73
- #
74
- # @param cbr_path [String] source CBR path
75
- # @param to [String] destination CBZ path
76
- # @param overwrite [Boolean] when false, raise {ArchiveError} if
77
- # +to+ already exists; default true
78
- # @param work_in [String, nil] see {.convert}
79
- # @return [String] the destination CBZ path
80
- # @raise [ArchiveError] when no extractor was configured or target cannot be written
81
- # @raise [ExtractionError] when the extractor fails
82
45
  def convert(cbr_path, to:, overwrite: true, work_in: nil)
83
- ensure_extractor!
84
46
  ensure_target_slot!(to, overwrite: overwrite)
85
47
  File.delete(to) if overwrite && File.exist?(to)
86
48
 
87
49
  in_workdir(work_in) do |temp_dir|
88
- @extractor.call(cbr_path, temp_dir)
50
+ extract(cbr_path, temp_dir)
89
51
  repack(temp_dir, to)
90
52
  end
91
53
 
@@ -94,9 +56,17 @@ module CbzTools
94
56
 
95
57
  private
96
58
 
97
- def ensure_extractor!
98
- raise ArchiveError,
99
- "No extractor configured; pass `extractor:` to CbrConverter.convert or CbrConverter.new" unless @extractor
59
+ def extract(cbr_path, destination)
60
+ unrar = SystemDependencies.unrar!
61
+ _, stderr, status = Open3.capture3(unrar, "x", "-o+", cbr_path, "#{destination}/")
62
+
63
+ unless status.success?
64
+ raise ArchiveError, "unrar failed: #{stderr}"
65
+ end
66
+ rescue SystemDependencies::MissingExecutableError => e
67
+ raise ArchiveError, e.message
68
+ rescue => e
69
+ raise ArchiveError, "Failed to extract CBR: #{e.message}"
100
70
  end
101
71
 
102
72
  def ensure_target_slot!(to, overwrite:)
@@ -133,4 +103,4 @@ module CbzTools
133
103
  zipfile.add(relative, file_path)
134
104
  end
135
105
  end
136
- end
106
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CbzTools
4
+ module SystemDependencies
5
+ class MissingExecutableError < StandardError; end
6
+
7
+ def self.find_executable(name)
8
+ extensions = Gem.win_platform? ? ENV.fetch("PATHEXT", "").split(";") : [""]
9
+
10
+ ENV.fetch("PATH", "").split(File::PATH_SEPARATOR).each do |directory|
11
+ extensions.each do |extension|
12
+ path = File.join(directory, "#{name}#{extension}")
13
+ return path if File.file?(path) && File.executable?(path)
14
+ end
15
+ end
16
+
17
+ nil
18
+ end
19
+
20
+ def self.unrar!
21
+ find_executable("unrar") ||
22
+ raise(
23
+ MissingExecutableError,
24
+ "`unrar` is required for CBR conversion but was not found in PATH. " \
25
+ "Install unrar using your system's package manager."
26
+ )
27
+ end
28
+ end
29
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CbzTools
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cbz_tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Figueroa
@@ -55,6 +55,7 @@ files:
55
55
  - lib/cbz_tools/comic_info.rb
56
56
  - lib/cbz_tools/error.rb
57
57
  - lib/cbz_tools/reader.rb
58
+ - lib/cbz_tools/system_dependencies.rb
58
59
  - lib/cbz_tools/version.rb
59
60
  - sig/cbz_tools.rbs
60
61
  homepage: https://github.com/dmfigueroa/cbz_tools
@@ -78,7 +79,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
79
  - - ">="
79
80
  - !ruby/object:Gem::Version
80
81
  version: '0'
81
- requirements: []
82
+ requirements:
83
+ - unrar executable available in PATH (for CBR conversion)
82
84
  rubygems_version: 4.0.17
83
85
  specification_version: 4
84
86
  summary: Read, extract, and edit metadata of CBZ comic archives, and convert CBR to