ruby_spriter 0.6.5
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 +7 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +217 -0
- data/Gemfile +17 -0
- data/LICENSE +21 -0
- data/README.md +561 -0
- data/bin/ruby_spriter +20 -0
- data/lib/ruby_spriter/cli.rb +249 -0
- data/lib/ruby_spriter/consolidator.rb +146 -0
- data/lib/ruby_spriter/dependency_checker.rb +174 -0
- data/lib/ruby_spriter/gimp_processor.rb +664 -0
- data/lib/ruby_spriter/metadata_manager.rb +116 -0
- data/lib/ruby_spriter/platform.rb +82 -0
- data/lib/ruby_spriter/processor.rb +251 -0
- data/lib/ruby_spriter/utils/file_helper.rb +57 -0
- data/lib/ruby_spriter/utils/output_formatter.rb +65 -0
- data/lib/ruby_spriter/utils/path_helper.rb +59 -0
- data/lib/ruby_spriter/version.rb +7 -0
- data/lib/ruby_spriter/video_processor.rb +139 -0
- data/lib/ruby_spriter.rb +31 -0
- data/ruby_spriter.gemspec +42 -0
- data/spec/fixtures/image_without_metadata.png +0 -0
- data/spec/fixtures/spritesheet_4x2.png +0 -0
- data/spec/fixtures/spritesheet_4x4.png +0 -0
- data/spec/fixtures/spritesheet_6x2.png +0 -0
- data/spec/fixtures/spritesheet_with_metadata.png +0 -0
- data/spec/fixtures/test_video.mp4 +0 -0
- data/spec/ruby_spriter/cli_spec.rb +1142 -0
- data/spec/ruby_spriter/consolidator_spec.rb +375 -0
- data/spec/ruby_spriter/dependency_checker_spec.rb +0 -0
- data/spec/ruby_spriter/gimp_processor_spec.rb +425 -0
- data/spec/ruby_spriter/metadata_manager_spec.rb +0 -0
- data/spec/ruby_spriter/platform_spec.rb +82 -0
- data/spec/ruby_spriter/processor_spec.rb +0 -0
- data/spec/ruby_spriter/utils/file_helper_spec.rb +71 -0
- data/spec/ruby_spriter/utils/output_formatter_spec.rb +0 -0
- data/spec/ruby_spriter/utils/path_helper_spec.rb +78 -0
- data/spec/ruby_spriter/video_processor_spec.rb +0 -0
- data/spec/spec_helper.rb +41 -0
- metadata +88 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'open3'
|
|
4
|
+
|
|
5
|
+
module RubySpriter
|
|
6
|
+
# Processes video files with FFmpeg
|
|
7
|
+
class VideoProcessor
|
|
8
|
+
attr_reader :options
|
|
9
|
+
|
|
10
|
+
def initialize(options = {})
|
|
11
|
+
@options = options
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Create spritesheet from video file
|
|
15
|
+
# @param video_file [String] Path to video file
|
|
16
|
+
# @param output_file [String] Path to output spritesheet
|
|
17
|
+
# @return [Hash] Processing results
|
|
18
|
+
def create_spritesheet(video_file, output_file)
|
|
19
|
+
Utils::FileHelper.validate_readable!(video_file)
|
|
20
|
+
|
|
21
|
+
Utils::OutputFormatter.header("Video Analysis")
|
|
22
|
+
duration = get_duration(video_file)
|
|
23
|
+
Utils::OutputFormatter.indent("Duration: #{duration.round(2)} seconds\n")
|
|
24
|
+
|
|
25
|
+
columns = options[:columns] || 4
|
|
26
|
+
frame_count = options[:frame_count] || 16
|
|
27
|
+
rows = (frame_count.to_f / columns).ceil
|
|
28
|
+
|
|
29
|
+
Utils::OutputFormatter.header("Creating Spritesheet")
|
|
30
|
+
|
|
31
|
+
temp_file = output_file.sub('.png', '_temp.png')
|
|
32
|
+
|
|
33
|
+
create_with_ffmpeg(video_file, temp_file, duration, columns, rows, frame_count)
|
|
34
|
+
|
|
35
|
+
# Embed metadata
|
|
36
|
+
MetadataManager.embed(
|
|
37
|
+
temp_file,
|
|
38
|
+
output_file,
|
|
39
|
+
columns: columns,
|
|
40
|
+
rows: rows,
|
|
41
|
+
frames: frame_count,
|
|
42
|
+
debug: options[:debug]
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Clean up temp file
|
|
46
|
+
File.delete(temp_file) if File.exist?(temp_file)
|
|
47
|
+
|
|
48
|
+
file_size = File.size(output_file)
|
|
49
|
+
|
|
50
|
+
# Display results with Godot instructions
|
|
51
|
+
display_spritesheet_results(output_file, file_size, columns, rows, frame_count)
|
|
52
|
+
|
|
53
|
+
{
|
|
54
|
+
output_file: output_file,
|
|
55
|
+
columns: columns,
|
|
56
|
+
rows: rows,
|
|
57
|
+
frames: frame_count,
|
|
58
|
+
size: file_size
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Get video duration in seconds
|
|
63
|
+
# @param video_file [String] Path to video file
|
|
64
|
+
# @return [Float] Duration in seconds
|
|
65
|
+
def get_duration(video_file)
|
|
66
|
+
cmd = [
|
|
67
|
+
'ffprobe',
|
|
68
|
+
'-v', 'error',
|
|
69
|
+
'-show_entries', 'format=duration',
|
|
70
|
+
'-of', 'default=noprint_wrappers=1:nokey=1',
|
|
71
|
+
Utils::PathHelper.quote_path(video_file)
|
|
72
|
+
].join(' ')
|
|
73
|
+
|
|
74
|
+
stdout, stderr, status = Open3.capture3(cmd)
|
|
75
|
+
|
|
76
|
+
unless status.success?
|
|
77
|
+
raise ProcessingError, "Could not determine video duration: #{stderr}"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
stdout.strip.to_f
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def create_with_ffmpeg(video_file, output_file, duration, columns, rows, frame_count)
|
|
86
|
+
fps = (frame_count / duration.to_f).round(6)
|
|
87
|
+
max_width = options[:max_width] || 320
|
|
88
|
+
|
|
89
|
+
Utils::OutputFormatter.indent("Layout: #{columns}×#{rows} grid (#{frame_count} frames)")
|
|
90
|
+
Utils::OutputFormatter.indent("Frame rate: #{fps} fps")
|
|
91
|
+
Utils::OutputFormatter.indent("Max frame width: #{max_width}px")
|
|
92
|
+
Utils::OutputFormatter.indent("Building spritesheet...")
|
|
93
|
+
|
|
94
|
+
filter_complex = [
|
|
95
|
+
"fps=#{fps}",
|
|
96
|
+
"scale=#{max_width}:-1:flags=lanczos",
|
|
97
|
+
"tile=#{columns}x#{rows}"
|
|
98
|
+
].join(',')
|
|
99
|
+
|
|
100
|
+
cmd = build_ffmpeg_command(video_file, output_file, filter_complex)
|
|
101
|
+
|
|
102
|
+
if options[:debug]
|
|
103
|
+
Utils::OutputFormatter.indent("DEBUG: ffmpeg command:")
|
|
104
|
+
Utils::OutputFormatter.indent(cmd)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
stdout, stderr, status = Open3.capture3(cmd)
|
|
108
|
+
|
|
109
|
+
unless status.success?
|
|
110
|
+
raise ProcessingError, "FFmpeg failed: #{stderr}"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
Utils::FileHelper.validate_exists!(output_file)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def build_ffmpeg_command(video_file, output_file, filter_complex)
|
|
117
|
+
[
|
|
118
|
+
'ffmpeg',
|
|
119
|
+
'-i', Utils::PathHelper.quote_path(video_file),
|
|
120
|
+
'-filter_complex', Utils::PathHelper.quote_arg(filter_complex),
|
|
121
|
+
'-frames:v', '1',
|
|
122
|
+
'-y',
|
|
123
|
+
Utils::PathHelper.quote_path(output_file),
|
|
124
|
+
'-hide_banner',
|
|
125
|
+
options[:debug] ? '-loglevel info' : '-loglevel error'
|
|
126
|
+
].join(' ')
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def display_spritesheet_results(output_file, file_size, columns, rows, frame_count)
|
|
130
|
+
Utils::OutputFormatter.success("Spritesheet created: #{output_file}")
|
|
131
|
+
Utils::OutputFormatter.indent("Size: #{Utils::FileHelper.format_size(file_size)}")
|
|
132
|
+
Utils::OutputFormatter.note("Metadata embedded: #{columns}×#{rows} grid (#{frame_count} frames)")
|
|
133
|
+
|
|
134
|
+
puts "\n 📊 Godot AnimatedSprite2D Settings:"
|
|
135
|
+
Utils::OutputFormatter.indent("HFrames = #{columns}")
|
|
136
|
+
Utils::OutputFormatter.indent("VFrames = #{rows}\n")
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
data/lib/ruby_spriter.rb
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Main module for Ruby Spriter
|
|
4
|
+
module RubySpriter
|
|
5
|
+
class Error < StandardError; end
|
|
6
|
+
class DependencyError < Error; end
|
|
7
|
+
class ProcessingError < Error; end
|
|
8
|
+
class ValidationError < Error; end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Load version first
|
|
12
|
+
require_relative 'ruby_spriter/version'
|
|
13
|
+
|
|
14
|
+
# Load utilities (no dependencies)
|
|
15
|
+
require_relative 'ruby_spriter/utils/path_helper'
|
|
16
|
+
require_relative 'ruby_spriter/utils/file_helper'
|
|
17
|
+
require_relative 'ruby_spriter/utils/output_formatter'
|
|
18
|
+
|
|
19
|
+
# Load core components
|
|
20
|
+
require_relative 'ruby_spriter/platform'
|
|
21
|
+
require_relative 'ruby_spriter/dependency_checker'
|
|
22
|
+
require_relative 'ruby_spriter/metadata_manager'
|
|
23
|
+
|
|
24
|
+
# Load processors
|
|
25
|
+
require_relative 'ruby_spriter/video_processor'
|
|
26
|
+
require_relative 'ruby_spriter/gimp_processor'
|
|
27
|
+
require_relative 'ruby_spriter/consolidator'
|
|
28
|
+
|
|
29
|
+
# Load orchestration
|
|
30
|
+
require_relative 'ruby_spriter/processor'
|
|
31
|
+
require_relative 'ruby_spriter/cli'
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/ruby_spriter/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'ruby_spriter'
|
|
7
|
+
spec.version = RubySpriter::VERSION
|
|
8
|
+
spec.authors = ['scooter-indie']
|
|
9
|
+
spec.email = ['scooter-indie@users.noreply.github.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'MP4 to Spritesheet converter with GIMP image processing'
|
|
12
|
+
spec.description = <<~DESC
|
|
13
|
+
Ruby Spriter is a cross-platform tool for creating spritesheets from video files
|
|
14
|
+
and processing them with GIMP. Features include background removal, scaling,
|
|
15
|
+
consolidation, and metadata management.
|
|
16
|
+
DESC
|
|
17
|
+
spec.homepage = 'https://github.com/scooter-indie/ruby-spriter'
|
|
18
|
+
spec.license = 'MIT'
|
|
19
|
+
spec.required_ruby_version = '>= 2.7.0'
|
|
20
|
+
|
|
21
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
22
|
+
spec.metadata['source_code_uri'] = 'https://github.com/scooter-indie/ruby-spriter'
|
|
23
|
+
spec.metadata['changelog_uri'] = 'https://github.com/scooter-indie/ruby-spriter/blob/main/CHANGELOG.md'
|
|
24
|
+
|
|
25
|
+
# Specify which files should be added to the gem when it is released.
|
|
26
|
+
spec.files = Dir.glob('{bin,lib,spec}/**/*') + %w[
|
|
27
|
+
README.md
|
|
28
|
+
CHANGELOG.md
|
|
29
|
+
LICENSE
|
|
30
|
+
Gemfile
|
|
31
|
+
ruby_spriter.gemspec
|
|
32
|
+
.rspec
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
spec.bindir = 'bin'
|
|
36
|
+
spec.executables = ['ruby_spriter']
|
|
37
|
+
spec.require_paths = ['lib']
|
|
38
|
+
|
|
39
|
+
# Runtime dependencies (none - uses only standard library + external tools)
|
|
40
|
+
|
|
41
|
+
# Development dependencies are in Gemfile
|
|
42
|
+
end
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|