|
6 | 6 | require 'async/barrier' |
7 | 7 | require 'async/semaphore' |
8 | 8 | require 'async/http/internet/instance' |
| 9 | +require 'zip' |
| 10 | +require 'tempfile' |
9 | 11 |
|
10 | 12 | module EmergeCLI |
11 | 13 | module Commands |
@@ -33,6 +35,8 @@ class Snapshots < EmergeCLI::Commands::GlobalOptions |
33 | 35 |
|
34 | 36 | option :profile, type: :boolean, default: false, desc: 'Enable performance profiling metrics' |
35 | 37 |
|
| 38 | + option :batch, type: :boolean, default: false, desc: 'Upload images in batch using zip file' |
| 39 | + |
36 | 40 | argument :image_paths, type: :array, required: false, desc: 'Paths to folders containing images' |
37 | 41 |
|
38 | 42 | def initialize(network: nil, git_info_provider: nil) |
@@ -178,6 +182,86 @@ def create_run |
178 | 182 | def upload_images(run_id, concurrency, image_files, client) |
179 | 183 | Logger.info 'Uploading images...' |
180 | 184 |
|
| 185 | + if @options[:batch] |
| 186 | + batch_upload_images(run_id, image_files, client) |
| 187 | + else |
| 188 | + individual_upload_images(run_id, concurrency, image_files, client) |
| 189 | + end |
| 190 | + end |
| 191 | + |
| 192 | + def batch_upload_images(run_id, image_files, client) |
| 193 | + Logger.info 'Preparing batch upload...' |
| 194 | + |
| 195 | + metadata_barrier = Async::Barrier.new |
| 196 | + metadata_semaphore = Async::Semaphore.new(10, parent: metadata_barrier) |
| 197 | + |
| 198 | + image_metadata = { |
| 199 | + manifestVersion: 1, |
| 200 | + images: {}, |
| 201 | + errors: [] |
| 202 | + } |
| 203 | + |
| 204 | + @profiler.measure('process_image_metadata') do |
| 205 | + image_files.each do |image_path| |
| 206 | + metadata_semaphore.async do |
| 207 | + file_info = client.parse_file_info(image_path) |
| 208 | + |
| 209 | + dimensions = @profiler.measure('chunky_png_processing') do |
| 210 | + datastream = ChunkyPNG::Datastream.from_file(image_path) |
| 211 | + { |
| 212 | + width: datastream.header_chunk.width, |
| 213 | + height: datastream.header_chunk.height |
| 214 | + } |
| 215 | + end |
| 216 | + |
| 217 | + metadata = { |
| 218 | + fileName: file_info[:file_name], |
| 219 | + groupName: file_info[:group_name], |
| 220 | + displayName: file_info[:variant_name], |
| 221 | + width: dimensions[:width], |
| 222 | + height: dimensions[:height] |
| 223 | + } |
| 224 | + |
| 225 | + image_name = File.basename(image_path, '.*') |
| 226 | + image_metadata[:images][image_name] = metadata |
| 227 | + end |
| 228 | + end |
| 229 | + |
| 230 | + metadata_barrier.wait |
| 231 | + end |
| 232 | + |
| 233 | + Tempfile.create(['snapshot_batch', '.zip']) do |zip_file| |
| 234 | + @profiler.measure('create_zip_file') do |
| 235 | + Zip::File.open(zip_file.path, Zip::File::CREATE) do |zipfile| |
| 236 | + zipfile.get_output_stream('manifest.json') { |f| f.write(JSON.generate(image_metadata)) } |
| 237 | + |
| 238 | + image_files.each do |image_path| |
| 239 | + image_name = File.basename(image_path) |
| 240 | + zipfile.add(image_name, image_path) |
| 241 | + end |
| 242 | + end |
| 243 | + end |
| 244 | + |
| 245 | + upload_url = @profiler.measure('create_batch_upload_url') do |
| 246 | + response = @network.post(path: '/v1/snapshots/run/batch-image', body: { run_id: run_id }) |
| 247 | + JSON.parse(response.read).fetch('zip_url') |
| 248 | + end |
| 249 | + |
| 250 | + Logger.info 'Uploading images...' |
| 251 | + Logger.debug "Uploading batch zip file to #{upload_url}" |
| 252 | + @profiler.measure('upload_batch_zip') do |
| 253 | + @network.put( |
| 254 | + path: upload_url, |
| 255 | + headers: { 'Content-Type' => 'application/zip' }, |
| 256 | + body: File.read(zip_file.path) |
| 257 | + ) |
| 258 | + end |
| 259 | + end |
| 260 | + ensure |
| 261 | + metadata_barrier&.stop |
| 262 | + end |
| 263 | + |
| 264 | + def individual_upload_images(run_id, concurrency, image_files, client) |
181 | 265 | post_image_barrier = Async::Barrier.new |
182 | 266 | post_image_semaphore = Async::Semaphore.new(concurrency, parent: post_image_barrier) |
183 | 267 |
|
|
0 commit comments