spaceship 0.7.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -0
- data/lib/spaceship/base.rb +3 -1
- data/lib/spaceship/client.rb +7 -3
- data/lib/spaceship/du/du_client.rb +100 -0
- data/lib/spaceship/du/upload_file.rb +45 -0
- data/lib/spaceship/du/utilities.rb +75 -0
- data/lib/spaceship/helper/net_http_generic_request.rb +5 -3
- data/lib/spaceship/portal/app.rb +0 -1
- data/lib/spaceship/portal/app_service.rb +3 -3
- data/lib/spaceship/portal/portal_client.rb +1 -2
- data/lib/spaceship/portal/provisioning_profile.rb +1 -2
- data/lib/spaceship/tunes/app_details.rb +6 -0
- data/lib/spaceship/tunes/app_image.rb +53 -0
- data/lib/spaceship/tunes/app_screenshot.rb +6 -17
- data/lib/spaceship/tunes/app_submission.rb +2 -14
- data/lib/spaceship/tunes/app_trailer.rb +70 -0
- data/lib/spaceship/tunes/app_version.rb +293 -30
- data/lib/spaceship/tunes/app_version_ref.rb +19 -0
- data/lib/spaceship/tunes/application.rb +12 -2
- data/lib/spaceship/tunes/build.rb +4 -1
- data/lib/spaceship/tunes/build_train.rb +0 -1
- data/lib/spaceship/tunes/device_type.rb +14 -0
- data/lib/spaceship/tunes/processing_build.rb +0 -1
- data/lib/spaceship/tunes/tester.rb +0 -2
- data/lib/spaceship/tunes/transit_app_file.rb +27 -0
- data/lib/spaceship/tunes/tunes.rb +11 -0
- data/lib/spaceship/tunes/tunes_client.rb +214 -13
- data/lib/spaceship/tunes/user_detail.rb +17 -0
- data/lib/spaceship/version.rb +1 -1
- metadata +25 -2
@@ -11,9 +11,6 @@ module Spaceship
|
|
11
11
|
# @return (AppVersion) The version to use for this submission
|
12
12
|
attr_accessor :version
|
13
13
|
|
14
|
-
# @return (String) The stage of this submission (start, complete)
|
15
|
-
attr_accessor :stage
|
16
|
-
|
17
14
|
# @return (Boolean) Submitted for Review
|
18
15
|
attr_accessor :submitted_for_review
|
19
16
|
|
@@ -116,11 +113,9 @@ module Spaceship
|
|
116
113
|
|
117
114
|
# @param application (Spaceship::Tunes::Application) The app this submission is for
|
118
115
|
def create(application, version)
|
119
|
-
|
120
|
-
attrs = client.send_app_submission(application.apple_id, version.raw_data, stage)
|
116
|
+
attrs = client.prepare_app_submissions(application.apple_id, application.edit_version.version_id)
|
121
117
|
attrs.merge!(application: application)
|
122
118
|
attrs.merge!(version: version)
|
123
|
-
attrs.merge!(stage: stage)
|
124
119
|
|
125
120
|
return self.factory(attrs)
|
126
121
|
end
|
@@ -128,20 +123,13 @@ module Spaceship
|
|
128
123
|
|
129
124
|
# Save and complete the app submission
|
130
125
|
def complete!
|
131
|
-
|
132
|
-
client.send_app_submission(application.apple_id, raw_data, @stage)
|
126
|
+
client.send_app_submission(application.apple_id, raw_data)
|
133
127
|
@submitted_for_review = true
|
134
128
|
end
|
135
129
|
|
136
|
-
# @return (String) An URL to this specific resource. You can enter this URL into your browser
|
137
|
-
def url
|
138
|
-
"https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/apps/#{self.application.apple_id}/version/submit/#{self.stage}"
|
139
|
-
end
|
140
|
-
|
141
130
|
def setup
|
142
131
|
@submitted_for_review = false
|
143
132
|
end
|
144
|
-
|
145
133
|
end
|
146
134
|
end
|
147
135
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Spaceship
|
2
|
+
module Tunes
|
3
|
+
# Represents a preview video hosted on iTunes Connect. Used for icons, screenshots, etc
|
4
|
+
class AppTrailer < TunesBase
|
5
|
+
attr_accessor :video_asset_token
|
6
|
+
|
7
|
+
attr_accessor :picture_asset_token
|
8
|
+
|
9
|
+
attr_accessor :descriptionXML
|
10
|
+
|
11
|
+
attr_accessor :preview_frame_time_code
|
12
|
+
|
13
|
+
attr_accessor :video_url
|
14
|
+
|
15
|
+
attr_accessor :preview_image_url
|
16
|
+
|
17
|
+
attr_accessor :full_sized_preview_image_url
|
18
|
+
|
19
|
+
attr_accessor :device_type
|
20
|
+
|
21
|
+
attr_accessor :language
|
22
|
+
|
23
|
+
attr_mapping(
|
24
|
+
'videoAssetToken' => :video_asset_token,
|
25
|
+
'pictureAssetToken' => :picture_asset_token,
|
26
|
+
'descriptionXML' => :descriptionXML,
|
27
|
+
'previewFrameTimeCode' => :preview_frame_time_code,
|
28
|
+
'isPortrait' => :is_portrait,
|
29
|
+
'videoUrl' => :video_url,
|
30
|
+
'previewImageUrl' => :preview_image_url,
|
31
|
+
'fullSizedPreviewImageUrl' => :full_sized_preview_image_url,
|
32
|
+
'contentType' => :content_type,
|
33
|
+
'videoStatus' => :video_status
|
34
|
+
)
|
35
|
+
|
36
|
+
class << self
|
37
|
+
def factory(attrs)
|
38
|
+
self.new(attrs)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def reset!(attrs = {})
|
43
|
+
update_raw_data!
|
44
|
+
({
|
45
|
+
video_asset_token: nil,
|
46
|
+
picture_asset_token: nil,
|
47
|
+
descriptionXML: nil,
|
48
|
+
preview_frame_time_code: nil,
|
49
|
+
is_portrait: nil,
|
50
|
+
video_url: nil,
|
51
|
+
preview_image_url: nil,
|
52
|
+
full_sized_preview_image_url: nil,
|
53
|
+
content_type: nil,
|
54
|
+
video_status: nil,
|
55
|
+
device_type: nil,
|
56
|
+
language: nil
|
57
|
+
}.merge(attrs)
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def update_raw_data!(hash)
|
64
|
+
hash.each do |k, v|
|
65
|
+
self.send("#{k}=", v)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -13,6 +13,9 @@ module Spaceship
|
|
13
13
|
# @return (String) The copyright information of this app
|
14
14
|
attr_accessor :copyright
|
15
15
|
|
16
|
+
# @return (String) The appType number of this version
|
17
|
+
attr_accessor :app_type
|
18
|
+
|
16
19
|
# @return (Spaceship::Tunes::AppStatus) What's the current status of this app
|
17
20
|
# e.g. Waiting for Review, Ready for Sale, ...
|
18
21
|
attr_reader :app_status
|
@@ -41,21 +44,21 @@ module Spaceship
|
|
41
44
|
# @return (Bool) Does the binary contain a watch binary?
|
42
45
|
attr_accessor :supports_apple_watch
|
43
46
|
|
44
|
-
# @return (
|
45
|
-
attr_accessor :
|
46
|
-
|
47
|
-
# @return (String) Name of the original file
|
48
|
-
attr_accessor :app_icon_original_name
|
49
|
-
|
50
|
-
# @return (String) URL to the full resolution 1024x1024 app icon
|
51
|
-
attr_accessor :watch_app_icon_url
|
47
|
+
# @return (Spaceship::Tunes::AppImage) the structure containing information about the large app icon (1024x1024)
|
48
|
+
attr_accessor :large_app_icon
|
52
49
|
|
53
|
-
# @return (
|
54
|
-
attr_accessor :
|
50
|
+
# @return (Spaceship::Tunes::AppImage) the structure containing information about the large watch icon (1024x1024)
|
51
|
+
attr_accessor :watch_app_icon
|
55
52
|
|
56
53
|
# @return (Integer) a unqiue ID for this version generated by iTunes Connect
|
57
54
|
attr_accessor :version_id
|
58
55
|
|
56
|
+
####
|
57
|
+
# GeoJson
|
58
|
+
####
|
59
|
+
# @return (Spaceship::Tunes::TransitAppFile) the structure containing information about the geo json. Can be nil
|
60
|
+
attr_accessor :transit_app_file
|
61
|
+
|
59
62
|
####
|
60
63
|
# App Review Information
|
61
64
|
####
|
@@ -84,7 +87,7 @@ module Spaceship
|
|
84
87
|
# Localized values
|
85
88
|
####
|
86
89
|
|
87
|
-
# @return (Array) Raw access the all available languages. You shouldn't use it
|
90
|
+
# @return (Array) Raw access the all available languages. You shouldn't use it probably
|
88
91
|
attr_accessor :languages
|
89
92
|
|
90
93
|
# @return (Hash) A hash representing the keywords in all languages
|
@@ -105,7 +108,11 @@ module Spaceship
|
|
105
108
|
# @return (Hash) Represents the screenshots of this app version (read-only)
|
106
109
|
attr_reader :screenshots
|
107
110
|
|
111
|
+
# @return (Hash) Represents the trailers of this app version (read-only)
|
112
|
+
attr_reader :trailers
|
113
|
+
|
108
114
|
attr_mapping({
|
115
|
+
'appType' => :app_type,
|
109
116
|
'canBetaTest' => :can_beta_test,
|
110
117
|
'canPrepareForUpload' => :can_prepare_for_upload,
|
111
118
|
'canRejectVersion' => :can_reject_version,
|
@@ -119,8 +126,9 @@ module Spaceship
|
|
119
126
|
'supportsAppleWatch' => :supports_apple_watch,
|
120
127
|
'versionId' => :version_id,
|
121
128
|
'version.value' => :version,
|
122
|
-
|
123
|
-
|
129
|
+
|
130
|
+
# GeoJson
|
131
|
+
# 'transitAppFile.value' => :transit_app_file
|
124
132
|
|
125
133
|
# App Review Information
|
126
134
|
'appReviewInfo.firstName.value' => :review_first_name,
|
@@ -195,6 +203,27 @@ module Spaceship
|
|
195
203
|
# languages
|
196
204
|
# end
|
197
205
|
|
206
|
+
# Returns an array of all builds that can be sent to review
|
207
|
+
def candidate_builds
|
208
|
+
res = client.candidate_builds(self.application.apple_id, self.version_id)
|
209
|
+
builds = []
|
210
|
+
res.each do |attrs|
|
211
|
+
next unless attrs["type"] == "BUILD" # I don't know if it can be something else.
|
212
|
+
builds << Tunes::Build.factory(attrs)
|
213
|
+
end
|
214
|
+
return builds
|
215
|
+
end
|
216
|
+
|
217
|
+
# Select a build to be submitted for Review.
|
218
|
+
# You have to pass a build you got from - candidate_builds
|
219
|
+
# Don't forget to call save! after calling this method
|
220
|
+
def select_build(build)
|
221
|
+
raw_data.set(['preReleaseBuildVersionString', 'value'], build.build_version)
|
222
|
+
raw_data.set(['preReleaseBuildTrainVersionString'], build.train_version)
|
223
|
+
raw_data.set(['preReleaseBuildUploadDate'], build.upload_date)
|
224
|
+
true
|
225
|
+
end
|
226
|
+
|
198
227
|
# Push all changes that were made back to iTunes Connect
|
199
228
|
def save!
|
200
229
|
client.update_app_version!(application.apple_id, is_live?, raw_data)
|
@@ -212,13 +241,157 @@ module Spaceship
|
|
212
241
|
# Properly parse the AppStatus
|
213
242
|
status = raw_data['status']
|
214
243
|
@app_status = Tunes::AppStatus.get_from_string(status)
|
244
|
+
setup_large_app_icon
|
245
|
+
setup_watch_app_icon
|
246
|
+
setup_transit_app_file
|
247
|
+
setup_screenshots
|
248
|
+
setup_trailers
|
249
|
+
end
|
215
250
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
@
|
251
|
+
# Uploads or removes the large icon
|
252
|
+
# @param icon_path (String): The path to the icon. Use nil to remove it
|
253
|
+
def upload_large_icon!(icon_path)
|
254
|
+
unless icon_path
|
255
|
+
@large_app_icon.reset!
|
256
|
+
return
|
257
|
+
end
|
258
|
+
upload_image = UploadFile.from_path icon_path
|
259
|
+
image_data = client.upload_large_icon(self, upload_image)
|
260
|
+
|
261
|
+
@large_app_icon.reset!({ asset_token: image_data['token'], original_file_name: upload_image.file_name })
|
262
|
+
end
|
263
|
+
|
264
|
+
# Uploads or removes the watch icon
|
265
|
+
# @param icon_path (String): The path to the icon. Use nil to remove it
|
266
|
+
def upload_watch_icon!(icon_path)
|
267
|
+
unless icon_path
|
268
|
+
@watch_app_icon.reset!
|
269
|
+
return
|
270
|
+
end
|
271
|
+
upload_image = UploadFile.from_path icon_path
|
272
|
+
image_data = client.upload_watch_icon(self, upload_image)
|
273
|
+
|
274
|
+
@watch_app_icon.reset!({ asset_token: image_data["token"], original_file_name: upload_image.file_name })
|
275
|
+
end
|
276
|
+
|
277
|
+
# Uploads or removes the transit app file
|
278
|
+
# @param icon_path (String): The path to the geojson file. Use nil to remove it
|
279
|
+
def upload_geojson!(geojson_path)
|
280
|
+
unless geojson_path
|
281
|
+
raw_data["transitAppFile"]["value"] = nil
|
282
|
+
@transit_app_file = nil
|
283
|
+
return
|
284
|
+
end
|
285
|
+
upload_file = UploadFile.from_path geojson_path
|
286
|
+
geojson_data = client.upload_geojson(self, upload_file)
|
287
|
+
|
288
|
+
@transit_app_file = Tunes::TransitAppFile.factory({}) if @transit_app_file.nil?
|
289
|
+
@transit_app_file .url = nil # response.headers['Location']
|
290
|
+
@transit_app_file.asset_token = geojson_data["token"]
|
291
|
+
@transit_app_file.name = upload_file.file_name
|
292
|
+
@transit_app_file.time_stamp = Time.now.to_i * 1000 # works without but...
|
293
|
+
end
|
294
|
+
|
295
|
+
# Uploads or removes a screenshot
|
296
|
+
# @param icon_path (String): The path to the screenshot. Use nil to remove it
|
297
|
+
# @param sort_order (Fixnum): The sort_order, from 1 to 5
|
298
|
+
# @param language (String): The language for this screenshot
|
299
|
+
# @param device (string): The device for this screenshot
|
300
|
+
def upload_screenshot!(screenshot_path, sort_order, language, device)
|
301
|
+
raise "sort_order must be positive" unless sort_order > 0
|
302
|
+
raise "sort_order must not be > 5" if sort_order > 5
|
303
|
+
# this will also check both language and device parameters
|
304
|
+
device_lang_screenshots = screenshots_data_for_language_and_device(language, device)["value"]
|
305
|
+
existing_sort_orders = device_lang_screenshots.map { |s| s["value"]["sortOrder"] }
|
306
|
+
if screenshot_path # adding / replacing
|
307
|
+
upload_file = UploadFile.from_path screenshot_path
|
308
|
+
screenshot_data = client.upload_screenshot(self, upload_file, device)
|
309
|
+
|
310
|
+
new_screenshot = {
|
311
|
+
"value" => {
|
312
|
+
"assetToken" => screenshot_data["token"],
|
313
|
+
"sortOrder" => sort_order,
|
314
|
+
"url" => nil,
|
315
|
+
"thumbNailUrl" => nil,
|
316
|
+
"originalFileName" => upload_file.file_name
|
317
|
+
}
|
318
|
+
}
|
319
|
+
if existing_sort_orders.include?(sort_order) # replace
|
320
|
+
device_lang_screenshots[existing_sort_orders.index(sort_order)] = new_screenshot
|
321
|
+
else # add
|
322
|
+
device_lang_screenshots << new_screenshot
|
323
|
+
end
|
324
|
+
else # removing
|
325
|
+
raise "cannot remove screenshot with non existing sort_order" unless existing_sort_orders.include?(sort_order)
|
326
|
+
device_lang_screenshots.delete_at(existing_sort_orders.index(sort_order))
|
221
327
|
end
|
328
|
+
setup_screenshots
|
329
|
+
end
|
330
|
+
|
331
|
+
# Uploads, removes a trailer video or change its preview image
|
332
|
+
#
|
333
|
+
# A preview image for the video is required by ITC and is usually automatically extracted by your browser.
|
334
|
+
# This method will either automatically extract it from the video (using `ffmpeg) or allow you
|
335
|
+
# to specify it using +preview_image_path+.
|
336
|
+
# If the preview image is specified, ffmpeg` will ot be used. The image resolution will be checked against
|
337
|
+
# expectations (which might be different from the trailer resolution.
|
338
|
+
#
|
339
|
+
# It is recommended to extract the preview image using the spaceship related tools in order to ensure
|
340
|
+
# the appropriate format and resolution are used.
|
341
|
+
#
|
342
|
+
# Note: if the video is already set, the +trailer_path+ is only used to grab the preview screenshot.
|
343
|
+
# Note: to extract its resolution and a screenshot preview, the `ffmpeg` tool will be used
|
344
|
+
#
|
345
|
+
# @param icon_path (String): The path to the screenshot. Use nil to remove it
|
346
|
+
# @param sort_order (Fixnum): The sort_order, from 1 to 5
|
347
|
+
# @param language (String): The language for this screenshot
|
348
|
+
# @param device (String): The device for this screenshot
|
349
|
+
# @param timestamp (String): The optional timestamp of the screenshot to grab
|
350
|
+
def upload_trailer!(trailer_path, language, device, timestamp = "05.00", preview_image_path = nil)
|
351
|
+
raise "No app trailer supported for iphone35" if device == 'iphone35'
|
352
|
+
|
353
|
+
device_lang_trailer = trailer_data_for_language_and_device(language, device)
|
354
|
+
if trailer_path # adding / replacing trailer / replacing preview
|
355
|
+
raise "Invalid timestamp #{timestamp}" if (timestamp =~ /^[0-9][0-9].[0-9][0-9]$/).nil?
|
356
|
+
|
357
|
+
if preview_image_path
|
358
|
+
check_preview_screenshot_resolution(preview_image_path, device)
|
359
|
+
video_preview_path = preview_image_path
|
360
|
+
else
|
361
|
+
# IDEA: optimization, we could avoid fetching the screenshot if the timestamp hasn't changed
|
362
|
+
video_preview_resolution = video_preview_resolution_for(device, trailer_path)
|
363
|
+
video_preview_path = Utilities.grab_video_preview(trailer_path, timestamp, video_preview_resolution)
|
364
|
+
end
|
365
|
+
video_preview_file = UploadFile.from_path video_preview_path
|
366
|
+
video_preview_data = client.upload_trailer_preview(self, video_preview_file)
|
367
|
+
|
368
|
+
trailer = device_lang_trailer["value"]
|
369
|
+
if trailer.nil? # add trailer
|
370
|
+
upload_file = UploadFile.from_path trailer_path
|
371
|
+
trailer_data = client.upload_trailer(self, upload_file)
|
372
|
+
trailer_data = trailer_data['responses'][0]
|
373
|
+
trailer = {
|
374
|
+
"videoAssetToken" => trailer_data["token"],
|
375
|
+
"descriptionXML" => trailer_data["descriptionDoc"],
|
376
|
+
"contentType" => upload_file.content_type
|
377
|
+
}
|
378
|
+
device_lang_trailer["value"] = trailer
|
379
|
+
end
|
380
|
+
# add / update preview
|
381
|
+
# different format required
|
382
|
+
ts = "00:00:#{timestamp}"
|
383
|
+
ts[8] = ':'
|
384
|
+
|
385
|
+
trailer.merge!({
|
386
|
+
"pictureAssetToken" => video_preview_data["token"],
|
387
|
+
"previewFrameTimeCode" => "#{ts}",
|
388
|
+
"isPortrait" => Utilities.portrait?(video_preview_path)
|
389
|
+
})
|
390
|
+
else # removing trailer
|
391
|
+
raise "cannot remove non existing trailer" if device_lang_trailer["value"].nil?
|
392
|
+
device_lang_trailer["value"] = nil
|
393
|
+
end
|
394
|
+
setup_trailers
|
222
395
|
end
|
223
396
|
|
224
397
|
# Prefill name, keywords, etc...
|
@@ -246,29 +419,119 @@ module Spaceship
|
|
246
419
|
|
247
420
|
private
|
248
421
|
|
422
|
+
def setup_large_app_icon
|
423
|
+
large_app_icon = raw_data["largeAppIcon"]["value"]
|
424
|
+
@large_app_icon = nil
|
425
|
+
@large_app_icon = Tunes::AppImage.factory(large_app_icon) if large_app_icon
|
426
|
+
end
|
427
|
+
|
428
|
+
def setup_watch_app_icon
|
429
|
+
watch_app_icon = raw_data["watchAppIcon"]["value"]
|
430
|
+
@watch_app_icon = nil
|
431
|
+
@watch_app_icon = Tunes::AppImage.factory(watch_app_icon) if watch_app_icon
|
432
|
+
end
|
433
|
+
|
434
|
+
def setup_transit_app_file
|
435
|
+
transit_app_file = raw_data["transitAppFile"]["value"]
|
436
|
+
@transit_app_file = nil
|
437
|
+
@transit_app_file = Tunes::TransitAppFile.factory(transit_app_file) if transit_app_file
|
438
|
+
end
|
439
|
+
|
440
|
+
def screenshots_data_for_language_and_device(language, device)
|
441
|
+
container_data_for_language_and_device("screenshots", language, device)
|
442
|
+
end
|
443
|
+
|
444
|
+
def trailer_data_for_language_and_device(language, device)
|
445
|
+
container_data_for_language_and_device("appTrailers", language, device)
|
446
|
+
end
|
447
|
+
|
448
|
+
def container_data_for_language_and_device(data_field, language, device)
|
449
|
+
raise "#{device} isn't a valid device name" unless DeviceType.exists?(device)
|
450
|
+
|
451
|
+
languages = raw_data_details.select { |d| d["language"] == language }
|
452
|
+
# IDEA: better error for non existing language
|
453
|
+
raise "#{language} isn't an activated language" unless languages.count > 0
|
454
|
+
lang_details = languages[0]
|
455
|
+
devices_details = lang_details[data_field]["value"]
|
456
|
+
raise "Unexpected state: missing device details for #{device}" unless devices_details.key? device
|
457
|
+
devices_details[device]
|
458
|
+
end
|
459
|
+
|
460
|
+
def setup_screenshots
|
461
|
+
@screenshots = {}
|
462
|
+
raw_data_details.each do |row|
|
463
|
+
# Now that's one language right here
|
464
|
+
@screenshots[row['language']] = setup_screenshots_for(row)
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
249
468
|
# generates the nested data structure to represent screenshots
|
250
|
-
def
|
251
|
-
screenshots = row.fetch(
|
469
|
+
def setup_screenshots_for(row)
|
470
|
+
screenshots = row.fetch("screenshots", {}).fetch("value", nil)
|
252
471
|
return [] unless screenshots
|
253
472
|
|
254
473
|
result = []
|
255
474
|
|
256
475
|
screenshots.each do |device_type, value|
|
257
|
-
value[
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
device_type: device_type,
|
265
|
-
language: row['language']
|
266
|
-
})
|
476
|
+
value["value"].each do |screenshot|
|
477
|
+
screenshot_data = screenshot["value"]
|
478
|
+
data = {
|
479
|
+
device_type: device_type,
|
480
|
+
language: row["language"]
|
481
|
+
}.merge(screenshot_data)
|
482
|
+
result << Tunes::AppScreenshot.factory(data)
|
267
483
|
end
|
268
484
|
end
|
269
485
|
|
270
486
|
return result
|
271
487
|
end
|
488
|
+
|
489
|
+
def setup_trailers
|
490
|
+
@trailers = {}
|
491
|
+
raw_data_details.each do |row|
|
492
|
+
# Now that's one language right here
|
493
|
+
@trailers[row["language"]] = setup_trailers_for(row)
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
# generates the nested data structure to represent trailers
|
498
|
+
def setup_trailers_for(row)
|
499
|
+
trailers = row.fetch("appTrailers", {}).fetch("value", nil)
|
500
|
+
return [] unless trailers
|
501
|
+
|
502
|
+
result = []
|
503
|
+
|
504
|
+
trailers.each do |device_type, value|
|
505
|
+
trailer_data = value["value"]
|
506
|
+
next if trailer_data.nil?
|
507
|
+
data = {
|
508
|
+
device_type: device_type,
|
509
|
+
language: row["language"]
|
510
|
+
}.merge(trailer_data)
|
511
|
+
result << Tunes::AppTrailer.factory(data)
|
512
|
+
end
|
513
|
+
|
514
|
+
return result
|
515
|
+
end
|
516
|
+
|
517
|
+
# identify the required resolution for this particular video screenshot
|
518
|
+
def video_preview_resolution_for(device, video_path)
|
519
|
+
is_portrait = Utilities.portrait?(video_path)
|
520
|
+
TunesClient.video_preview_resolution_for(device, is_portrait)
|
521
|
+
end
|
522
|
+
|
523
|
+
# ensure the specified preview screenshot has the expected resolution the specified target +device+
|
524
|
+
def check_preview_screenshot_resolution(preview_screenshot_path, device)
|
525
|
+
is_portrait = Utilities.portrait?(preview_screenshot_path)
|
526
|
+
expected_resolution = TunesClient.video_preview_resolution_for(device, is_portrait)
|
527
|
+
actual_resolution = Utilities.resolution(preview_screenshot_path)
|
528
|
+
orientation = is_portrait ? "portrait" : "landscape"
|
529
|
+
raise "Invalid #{orientation} screenshot resolution for device #{device}. Should be #{expected_resolution}" unless (actual_resolution == expected_resolution)
|
530
|
+
end
|
531
|
+
|
532
|
+
def raw_data_details
|
533
|
+
raw_data["details"]["value"]
|
534
|
+
end
|
272
535
|
end
|
273
536
|
end
|
274
537
|
end
|