ruby_spriter 0.6.5 → 0.6.7
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/CHANGELOG.md +188 -0
- data/README.md +374 -33
- data/lib/ruby_spriter/batch_processor.rb +212 -0
- data/lib/ruby_spriter/cli.rb +369 -6
- data/lib/ruby_spriter/compression_manager.rb +101 -0
- data/lib/ruby_spriter/consolidator.rb +33 -0
- data/lib/ruby_spriter/gimp_processor.rb +6 -3
- data/lib/ruby_spriter/processor.rb +661 -26
- data/lib/ruby_spriter/utils/file_helper.rb +25 -0
- data/lib/ruby_spriter/utils/spritesheet_splitter.rb +86 -0
- data/lib/ruby_spriter/version.rb +2 -2
- data/lib/ruby_spriter/video_processor.rb +7 -7
- data/lib/ruby_spriter.rb +3 -0
- data/spec/ruby_spriter/batch_processor_spec.rb +200 -0
- data/spec/ruby_spriter/cli_spec.rb +750 -0
- data/spec/ruby_spriter/compression_manager_spec.rb +157 -0
- data/spec/ruby_spriter/consolidator_spec.rb +163 -0
- data/spec/ruby_spriter/processor_spec.rb +735 -0
- data/spec/ruby_spriter/utils/file_helper_spec.rb +80 -1
- data/spec/ruby_spriter/utils/spritesheet_splitter_spec.rb +104 -0
- data/spec/ruby_spriter/video_processor_spec.rb +29 -0
- metadata +8 -2
|
@@ -62,10 +62,89 @@ RSpec.describe RubySpriter::Utils::FileHelper do
|
|
|
62
62
|
it 'validates file exists and is readable' do
|
|
63
63
|
file = File.join(@test_dir, 'test.txt')
|
|
64
64
|
File.write(file, 'test')
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
expect {
|
|
67
67
|
described_class.validate_readable!(file)
|
|
68
68
|
}.not_to raise_error
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
|
+
|
|
72
|
+
describe '.unique_filename' do
|
|
73
|
+
it 'returns original filename when file does not exist' do
|
|
74
|
+
result = described_class.unique_filename('/path/to/output.png')
|
|
75
|
+
expect(result).to eq('/path/to/output.png')
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'adds timestamp when file exists' do
|
|
79
|
+
file = File.join(@test_dir, 'existing.png')
|
|
80
|
+
File.write(file, 'test')
|
|
81
|
+
|
|
82
|
+
result = described_class.unique_filename(file)
|
|
83
|
+
expect(result).to match(/existing_\d{8}_\d{6}_\d{3}\.png$/)
|
|
84
|
+
expect(result).not_to eq(file)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it 'handles files with multiple dots in name' do
|
|
88
|
+
file = File.join(@test_dir, 'my.sprite.sheet.png')
|
|
89
|
+
File.write(file, 'test')
|
|
90
|
+
|
|
91
|
+
result = described_class.unique_filename(file)
|
|
92
|
+
expect(result).to match(/my\.sprite\.sheet_\d{8}_\d{6}_\d{3}\.png$/)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it 'preserves directory path' do
|
|
96
|
+
file = File.join(@test_dir, 'subdir', 'output.png')
|
|
97
|
+
FileUtils.mkdir_p(File.dirname(file))
|
|
98
|
+
File.write(file, 'test')
|
|
99
|
+
|
|
100
|
+
result = described_class.unique_filename(file)
|
|
101
|
+
expect(result).to start_with(File.join(@test_dir, 'subdir'))
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it 'generates different filenames for consecutive calls' do
|
|
105
|
+
file = File.join(@test_dir, 'test.png')
|
|
106
|
+
File.write(file, 'test')
|
|
107
|
+
|
|
108
|
+
result1 = described_class.unique_filename(file)
|
|
109
|
+
sleep(0.01) # Small delay to ensure different timestamps
|
|
110
|
+
result2 = described_class.unique_filename(file)
|
|
111
|
+
|
|
112
|
+
expect(result1).not_to eq(result2)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
describe '.ensure_unique_output' do
|
|
117
|
+
it 'returns original path when overwrite is true' do
|
|
118
|
+
file = File.join(@test_dir, 'output.png')
|
|
119
|
+
File.write(file, 'test')
|
|
120
|
+
|
|
121
|
+
result = described_class.ensure_unique_output(file, overwrite: true)
|
|
122
|
+
expect(result).to eq(file)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it 'returns original path when file does not exist and overwrite is false' do
|
|
126
|
+
file = File.join(@test_dir, 'new_output.png')
|
|
127
|
+
|
|
128
|
+
result = described_class.ensure_unique_output(file, overwrite: false)
|
|
129
|
+
expect(result).to eq(file)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it 'returns unique filename when file exists and overwrite is false' do
|
|
133
|
+
file = File.join(@test_dir, 'existing_output.png')
|
|
134
|
+
File.write(file, 'test')
|
|
135
|
+
|
|
136
|
+
result = described_class.ensure_unique_output(file, overwrite: false)
|
|
137
|
+
expect(result).to match(/existing_output_\d{8}_\d{6}_\d{3}\.png$/)
|
|
138
|
+
expect(result).not_to eq(file)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it 'defaults to overwrite false when not specified' do
|
|
142
|
+
file = File.join(@test_dir, 'default_test.png')
|
|
143
|
+
File.write(file, 'test')
|
|
144
|
+
|
|
145
|
+
result = described_class.ensure_unique_output(file)
|
|
146
|
+
expect(result).not_to eq(file)
|
|
147
|
+
expect(result).to match(/default_test_\d{8}_\d{6}_\d{3}\.png$/)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
71
150
|
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe RubySpriter::Utils::SpritesheetSplitter do
|
|
6
|
+
describe '#split_into_frames' do
|
|
7
|
+
let(:spritesheet_file) { 'spritesheet.png' }
|
|
8
|
+
let(:output_dir) { '/tmp/frames' }
|
|
9
|
+
let(:columns) { 4 }
|
|
10
|
+
let(:rows) { 4 }
|
|
11
|
+
let(:frames) { 16 }
|
|
12
|
+
let(:tile_width) { 100 }
|
|
13
|
+
let(:tile_height) { 100 }
|
|
14
|
+
|
|
15
|
+
let(:splitter) { described_class.new }
|
|
16
|
+
|
|
17
|
+
before do
|
|
18
|
+
allow(FileUtils).to receive(:mkdir_p)
|
|
19
|
+
allow(File).to receive(:exist?).and_return(true)
|
|
20
|
+
allow(Open3).to receive(:capture3).and_return(['', '', instance_double(Process::Status, success?: true)])
|
|
21
|
+
|
|
22
|
+
# Mock ImageMagick identify to return dimensions
|
|
23
|
+
allow(Open3).to receive(:capture3).with(/identify/).and_return(
|
|
24
|
+
["400x400\n", '', instance_double(Process::Status, success?: true)]
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'creates output directory for frames' do
|
|
29
|
+
expect(FileUtils).to receive(:mkdir_p).with(output_dir)
|
|
30
|
+
|
|
31
|
+
splitter.split_into_frames(spritesheet_file, output_dir, columns, rows, frames)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'extracts each frame with FR prefix and zero-padded numbers' do
|
|
35
|
+
# Expect ImageMagick convert commands for each frame
|
|
36
|
+
expect(Open3).to receive(:capture3).with(/identify/).once.and_return(
|
|
37
|
+
["400x400\n", '', instance_double(Process::Status, success?: true)]
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
(1..frames).each do |i|
|
|
41
|
+
expect(Open3).to receive(:capture3).with(/convert.*FR#{format('%03d', i)}_/).and_return(
|
|
42
|
+
['', '', instance_double(Process::Status, success?: true)]
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
splitter.split_into_frames(spritesheet_file, output_dir, columns, rows, frames)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'calculates tile dimensions from spritesheet size and grid' do
|
|
50
|
+
# For 400x400 image with 4x4 grid, tiles should be 100x100
|
|
51
|
+
expect(Open3).to receive(:capture3).with(/identify/).and_return(
|
|
52
|
+
["400x400\n", '', instance_double(Process::Status, success?: true)]
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Check that crop parameters use 100x100
|
|
56
|
+
expect(Open3).to receive(:capture3).with(/100x100\+0\+0/).and_return(
|
|
57
|
+
['', '', instance_double(Process::Status, success?: true)]
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
allow(Open3).to receive(:capture3).and_return(['', '', instance_double(Process::Status, success?: true)])
|
|
61
|
+
|
|
62
|
+
splitter.split_into_frames(spritesheet_file, output_dir, columns, rows, 1)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'includes spritesheet basename in frame output names' do
|
|
66
|
+
basename = File.basename(spritesheet_file, '.*')
|
|
67
|
+
|
|
68
|
+
# Mock identify first
|
|
69
|
+
allow(Open3).to receive(:capture3).with(/identify/).and_return(
|
|
70
|
+
["400x400\n", '', instance_double(Process::Status, success?: true)]
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Expect convert with frame filename
|
|
74
|
+
expect(Open3).to receive(:capture3).with(/FR001_#{basename}\.png/).and_return(
|
|
75
|
+
['', '', instance_double(Process::Status, success?: true)]
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
splitter.split_into_frames(spritesheet_file, output_dir, columns, rows, 1)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'raises ProcessingError when ImageMagick fails' do
|
|
82
|
+
allow(Open3).to receive(:capture3).with(/identify/).and_return(
|
|
83
|
+
["400x400\n", '', instance_double(Process::Status, success?: true)]
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
allow(Open3).to receive(:capture3).with(/convert/).and_return(
|
|
87
|
+
['', 'Error message', instance_double(Process::Status, success?: false)]
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
expect {
|
|
91
|
+
splitter.split_into_frames(spritesheet_file, output_dir, columns, rows, 1)
|
|
92
|
+
}.to raise_error(RubySpriter::ProcessingError, /Could not extract frame/)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it 'displays progress information' do
|
|
96
|
+
expect(RubySpriter::Utils::OutputFormatter).to receive(:header).with(/Extracting Frames/)
|
|
97
|
+
expect(RubySpriter::Utils::OutputFormatter).to receive(:indent).with(/Splitting spritesheet into #{frames} frames/)
|
|
98
|
+
expect(RubySpriter::Utils::OutputFormatter).to receive(:indent).with(/Output directory:/)
|
|
99
|
+
expect(RubySpriter::Utils::OutputFormatter).to receive(:indent).with(/Frames extracted successfully/)
|
|
100
|
+
|
|
101
|
+
splitter.split_into_frames(spritesheet_file, output_dir, columns, rows, frames)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe RubySpriter::VideoProcessor do
|
|
6
|
+
describe '#create_spritesheet' do
|
|
7
|
+
let(:video_file) { 'test_video.mp4' }
|
|
8
|
+
let(:output_file) { 'spritesheet.png' }
|
|
9
|
+
let(:processor) { described_class.new(frame_count: 16, columns: 4) }
|
|
10
|
+
|
|
11
|
+
before do
|
|
12
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:validate_readable!)
|
|
13
|
+
allow(File).to receive(:size).and_return(1000)
|
|
14
|
+
allow(File).to receive(:exist?).and_return(true)
|
|
15
|
+
allow(File).to receive(:delete)
|
|
16
|
+
allow(RubySpriter::MetadataManager).to receive(:embed)
|
|
17
|
+
allow(Open3).to receive(:capture3).and_return(['2.0', '', instance_double(Process::Status, success?: true)])
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'creates spritesheet from video file' do
|
|
21
|
+
result = processor.create_spritesheet(video_file, output_file)
|
|
22
|
+
|
|
23
|
+
expect(result[:output_file]).to eq(output_file)
|
|
24
|
+
expect(result[:columns]).to eq(4)
|
|
25
|
+
expect(result[:rows]).to eq(4)
|
|
26
|
+
expect(result[:frames]).to eq(16)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_spriter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- scooter-indie
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-10-
|
|
11
|
+
date: 2025-10-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: |
|
|
14
14
|
Ruby Spriter is a cross-platform tool for creating spritesheets from video files
|
|
@@ -28,7 +28,9 @@ files:
|
|
|
28
28
|
- README.md
|
|
29
29
|
- bin/ruby_spriter
|
|
30
30
|
- lib/ruby_spriter.rb
|
|
31
|
+
- lib/ruby_spriter/batch_processor.rb
|
|
31
32
|
- lib/ruby_spriter/cli.rb
|
|
33
|
+
- lib/ruby_spriter/compression_manager.rb
|
|
32
34
|
- lib/ruby_spriter/consolidator.rb
|
|
33
35
|
- lib/ruby_spriter/dependency_checker.rb
|
|
34
36
|
- lib/ruby_spriter/gimp_processor.rb
|
|
@@ -38,6 +40,7 @@ files:
|
|
|
38
40
|
- lib/ruby_spriter/utils/file_helper.rb
|
|
39
41
|
- lib/ruby_spriter/utils/output_formatter.rb
|
|
40
42
|
- lib/ruby_spriter/utils/path_helper.rb
|
|
43
|
+
- lib/ruby_spriter/utils/spritesheet_splitter.rb
|
|
41
44
|
- lib/ruby_spriter/version.rb
|
|
42
45
|
- lib/ruby_spriter/video_processor.rb
|
|
43
46
|
- ruby_spriter.gemspec
|
|
@@ -47,7 +50,9 @@ files:
|
|
|
47
50
|
- spec/fixtures/spritesheet_6x2.png
|
|
48
51
|
- spec/fixtures/spritesheet_with_metadata.png
|
|
49
52
|
- spec/fixtures/test_video.mp4
|
|
53
|
+
- spec/ruby_spriter/batch_processor_spec.rb
|
|
50
54
|
- spec/ruby_spriter/cli_spec.rb
|
|
55
|
+
- spec/ruby_spriter/compression_manager_spec.rb
|
|
51
56
|
- spec/ruby_spriter/consolidator_spec.rb
|
|
52
57
|
- spec/ruby_spriter/dependency_checker_spec.rb
|
|
53
58
|
- spec/ruby_spriter/gimp_processor_spec.rb
|
|
@@ -57,6 +62,7 @@ files:
|
|
|
57
62
|
- spec/ruby_spriter/utils/file_helper_spec.rb
|
|
58
63
|
- spec/ruby_spriter/utils/output_formatter_spec.rb
|
|
59
64
|
- spec/ruby_spriter/utils/path_helper_spec.rb
|
|
65
|
+
- spec/ruby_spriter/utils/spritesheet_splitter_spec.rb
|
|
60
66
|
- spec/ruby_spriter/video_processor_spec.rb
|
|
61
67
|
- spec/spec_helper.rb
|
|
62
68
|
homepage: https://github.com/scooter-indie/ruby-spriter
|