codeplay-common 4.6.7 → 4.6.8
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.
- package/.gitattributes +2 -2
- package/LICENSE +21 -21
- package/README.md +120 -120
- package/files/buildCodeplay/codeplayBeforeBuild-6.3.js +4006 -4006
- package/files/buildCodeplay/packageidBaseModification-1.4.js +445 -445
- package/files/buildCodeplay/setSplashAnimation-1.4.js +156 -156
- package/files/buildCodeplay/versions.json +23 -23
- package/files/finalrelease +966 -966
- package/files/generate-ai-promo-video.js +23 -23
- package/files/iap-install-2.js +145 -145
- package/files/ionic.config.json +6 -6
- package/files/scripts/setup-baseline-profile.js +262 -262
- package/files/take-screen-image.js +384 -23
- package/files/take-screen-video.js +644 -644
- package/package.json +15 -15
- package/scripts/sync-files.js +103 -103
- package/scripts/uninstall.js +109 -109
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
//node take-screen-image.js --delete 3
|
|
2
2
|
//node take-screen-image.js --delete 3,4,5
|
|
3
|
-
|
|
3
|
+
//node take-screen-image.js --profiles vivo-store //only taking screenshot for vivo-store - Not only vivo, we can do this to all of our store images.
|
|
4
|
+
//node take-screen-image.js --vivo-existing //Used if we already took the screenshot and those vivo screenshots are not there then execute this to resize the screenshot for vivo store.
|
|
5
|
+
//node take-screen-image.js --oppo-existing //Used if we already took the screenshot and those oppo screenshots are not there then execute this to resize the screenshot for Oppo store.
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
const fs = require('node:fs');
|
|
7
9
|
const path = require('node:path');
|
|
8
10
|
const readline = require('node:readline/promises');
|
|
9
11
|
const { spawn, spawnSync } = require('node:child_process');
|
|
12
|
+
const sharp = require('sharp');
|
|
10
13
|
|
|
11
14
|
const screenshotDirectory = path.join(__dirname, 'Auto-Screenshot', 'Store-image-Screenshot');
|
|
12
15
|
const inFrameDirectory = path.join(__dirname, 'Auto-Screenshot', 'Store-image-Frame');
|
|
@@ -53,6 +56,52 @@ const deviceProfiles = [
|
|
|
53
56
|
orientationSensitive: true,
|
|
54
57
|
mobile: true,
|
|
55
58
|
},
|
|
59
|
+
{
|
|
60
|
+
name: 'vivo-store',
|
|
61
|
+
label: 'Vivo Store portrait screenshot',
|
|
62
|
+
outputDirectory: 'Vivo-Store',
|
|
63
|
+
frameType: 'android-phone',
|
|
64
|
+
width: 360,
|
|
65
|
+
height: 720,
|
|
66
|
+
deviceScaleFactor: 3,
|
|
67
|
+
framedWidth: 1080,
|
|
68
|
+
framedHeight: 2160,
|
|
69
|
+
orientationSensitive: false,
|
|
70
|
+
mobile: true,
|
|
71
|
+
storeRequirements: {
|
|
72
|
+
maxFileSizeBytes: 5 * 1024 * 1024,
|
|
73
|
+
minPixels: 320,
|
|
74
|
+
maxPixels: 3840,
|
|
75
|
+
minAspectRatio: 0.5,
|
|
76
|
+
maxAspectRatio: 2,
|
|
77
|
+
minImages: 4,
|
|
78
|
+
maxImages: 8,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'oppo-store',
|
|
83
|
+
label: 'Oppo Store portrait screenshot',
|
|
84
|
+
outputDirectory: 'Oppo-Store',
|
|
85
|
+
frameType: 'android-phone',
|
|
86
|
+
width: 360,
|
|
87
|
+
height: 640,
|
|
88
|
+
deviceScaleFactor: 3,
|
|
89
|
+
framedWidth: 1080,
|
|
90
|
+
framedHeight: 1920,
|
|
91
|
+
orientationSensitive: false,
|
|
92
|
+
mobile: true,
|
|
93
|
+
outputFormat: 'jpeg',
|
|
94
|
+
outputExtension: 'jpg',
|
|
95
|
+
storeRequirements: {
|
|
96
|
+
maxFileSizeBytes: 3 * 1024 * 1024,
|
|
97
|
+
minPixels: 320,
|
|
98
|
+
maxPixels: 1920,
|
|
99
|
+
minAspectRatio: 0.5,
|
|
100
|
+
maxAspectRatio: 0.6,
|
|
101
|
+
minImages: 3,
|
|
102
|
+
maxImages: 5,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
56
105
|
{
|
|
57
106
|
name: 'android-tablet-7-inch',
|
|
58
107
|
aliases: ['android-tablet'],
|
|
@@ -353,31 +402,33 @@ function getSelectedOutputDirectories(selectedProfiles) {
|
|
|
353
402
|
}
|
|
354
403
|
|
|
355
404
|
function getNextNumberedFileName(selectedProfiles) {
|
|
405
|
+
const extension = selectedProfiles[0]?.outputExtension || 'png';
|
|
356
406
|
const highestNumber = getSelectedOutputDirectories(selectedProfiles)
|
|
357
407
|
.filter((directory) => fs.existsSync(directory))
|
|
358
408
|
.reduce((highestAcrossDirectories, directory) => {
|
|
359
409
|
const highestInDirectory = fs.readdirSync(directory, { withFileTypes: true })
|
|
360
410
|
.filter((entry) => entry.isFile())
|
|
361
411
|
.reduce((highest, entry) => {
|
|
362
|
-
const match = entry.name.match(
|
|
412
|
+
const match = entry.name.match(new RegExp(`^(\\d+)\\.${extension}$`, 'i'));
|
|
363
413
|
return match ? Math.max(highest, Number(match[1])) : highest;
|
|
364
414
|
}, 0);
|
|
365
415
|
|
|
366
416
|
return Math.max(highestAcrossDirectories, highestInDirectory);
|
|
367
417
|
}, 0);
|
|
368
418
|
|
|
369
|
-
return `${highestNumber + 1}
|
|
419
|
+
return `${highestNumber + 1}.${extension}`;
|
|
370
420
|
}
|
|
371
421
|
|
|
372
422
|
function getUniqueFileName(prefix, selectedProfiles) {
|
|
423
|
+
const extension = selectedProfiles[0]?.outputExtension || 'png';
|
|
373
424
|
const outputDirectories = getSelectedOutputDirectories(selectedProfiles);
|
|
374
|
-
let fileName = `${prefix}
|
|
425
|
+
let fileName = `${prefix}.${extension}`;
|
|
375
426
|
let duplicateNumber = 2;
|
|
376
427
|
|
|
377
428
|
while (outputDirectories.some((directory) => (
|
|
378
429
|
fs.existsSync(path.join(directory, fileName))
|
|
379
430
|
))) {
|
|
380
|
-
fileName = `${prefix}-${duplicateNumber}
|
|
431
|
+
fileName = `${prefix}-${duplicateNumber}.${extension}`;
|
|
381
432
|
duplicateNumber += 1;
|
|
382
433
|
}
|
|
383
434
|
|
|
@@ -385,7 +436,7 @@ function getUniqueFileName(prefix, selectedProfiles) {
|
|
|
385
436
|
}
|
|
386
437
|
|
|
387
438
|
function getScreenshotFileName(fileNameInput, selectedProfiles) {
|
|
388
|
-
const inputWithoutExtension = fileNameInput.trim().replace(/\.png$/i, '');
|
|
439
|
+
const inputWithoutExtension = fileNameInput.trim().replace(/\.(png|jpe?g)$/i, '');
|
|
389
440
|
|
|
390
441
|
if (!inputWithoutExtension) {
|
|
391
442
|
return getNextNumberedFileName(selectedProfiles);
|
|
@@ -411,6 +462,8 @@ function parseArguments() {
|
|
|
411
462
|
samsungAssets: false,
|
|
412
463
|
dryRun: false,
|
|
413
464
|
frameExisting: false,
|
|
465
|
+
vivoExisting: false,
|
|
466
|
+
oppoExisting: false,
|
|
414
467
|
help: false,
|
|
415
468
|
list: false,
|
|
416
469
|
deleteRequested: false,
|
|
@@ -430,6 +483,10 @@ function parseArguments() {
|
|
|
430
483
|
options.dryRun = true;
|
|
431
484
|
} else if (argument === '--frame-existing') {
|
|
432
485
|
options.frameExisting = true;
|
|
486
|
+
} else if (argument === '--vivo-existing') {
|
|
487
|
+
options.vivoExisting = true;
|
|
488
|
+
} else if (argument === '--oppo-existing') {
|
|
489
|
+
options.oppoExisting = true;
|
|
433
490
|
} else if (argument === '--help' || argument === '-h') {
|
|
434
491
|
options.help = true;
|
|
435
492
|
} else if (argument === '--list') {
|
|
@@ -460,13 +517,20 @@ function printProfiles() {
|
|
|
460
517
|
const outputWidth = profile.width * profile.deviceScaleFactor;
|
|
461
518
|
const outputHeight = profile.height * profile.deviceScaleFactor;
|
|
462
519
|
const framedSize = profile.framedWidth
|
|
463
|
-
? `, framed
|
|
520
|
+
? `, framed ${(profile.outputFormat || 'png').toUpperCase()} ${profile.framedWidth}x${profile.framedHeight}`
|
|
464
521
|
: '';
|
|
465
522
|
console.log(
|
|
466
523
|
`${profile.name.padEnd(15)} ${profile.label} `
|
|
467
524
|
+ `(folder ${profile.outputDirectory}, viewport ${profile.width}x${profile.height}, `
|
|
468
|
-
+
|
|
525
|
+
+ `${(profile.outputFormat || 'png').toUpperCase()} ${outputWidth}x${outputHeight}${framedSize})`,
|
|
469
526
|
);
|
|
527
|
+
if (profile.storeRequirements) {
|
|
528
|
+
console.log(
|
|
529
|
+
` Store limits: ${profile.storeRequirements.minImages}-${profile.storeRequirements.maxImages} images, `
|
|
530
|
+
+ `max ${profile.storeRequirements.maxFileSizeBytes / (1024 * 1024)} MB, `
|
|
531
|
+
+ `width/height ${profile.storeRequirements.minAspectRatio}-${profile.storeRequirements.maxAspectRatio}:1`,
|
|
532
|
+
);
|
|
533
|
+
}
|
|
470
534
|
}
|
|
471
535
|
}
|
|
472
536
|
|
|
@@ -478,6 +542,8 @@ function printHelp() {
|
|
|
478
542
|
console.log(' node take-screen-image.js --profiles iphone,ipad');
|
|
479
543
|
console.log(' node take-screen-image.js --prefix home --profiles android-phone,ipad');
|
|
480
544
|
console.log(' node take-screen-image.js --frame-existing');
|
|
545
|
+
console.log(' node take-screen-image.js --vivo-existing');
|
|
546
|
+
console.log(' node take-screen-image.js --oppo-existing');
|
|
481
547
|
console.log(' node take-screen-image.js --amazon-assets');
|
|
482
548
|
console.log(' node take-screen-image.js --samsung-assets');
|
|
483
549
|
console.log(' node take-screen-image.js --dry-run');
|
|
@@ -1154,9 +1220,84 @@ function getFrameAdornment(frameType) {
|
|
|
1154
1220
|
return '<div class="xr-status"></div>';
|
|
1155
1221
|
}
|
|
1156
1222
|
|
|
1157
|
-
function
|
|
1158
|
-
const screenshotDataUrl = `data:image
|
|
1159
|
-
const
|
|
1223
|
+
function createStoreDeviceFrameHtml(profile, screenshotBuffer, imageWidth, imageHeight, canvasWidth, canvasHeight, imageFormat) {
|
|
1224
|
+
const screenshotDataUrl = `data:image/${imageFormat === 'jpeg' ? 'jpeg' : 'png'};base64,${screenshotBuffer.toString('base64')}`;
|
|
1225
|
+
const screenWidth = Math.floor(Math.min(canvasWidth - 160, (canvasHeight - 180) * imageWidth / imageHeight));
|
|
1226
|
+
const accent = profile.name === 'oppo-store' ? '#76e4c2' : '#9daaff';
|
|
1227
|
+
|
|
1228
|
+
return `<!doctype html>
|
|
1229
|
+
<html>
|
|
1230
|
+
<head>
|
|
1231
|
+
<meta charset="utf-8" />
|
|
1232
|
+
<style>
|
|
1233
|
+
* { box-sizing: border-box; }
|
|
1234
|
+
html, body { width: 100%; height: 100%; margin: 0; overflow: hidden; }
|
|
1235
|
+
body {
|
|
1236
|
+
display: grid;
|
|
1237
|
+
place-items: center;
|
|
1238
|
+
background: radial-gradient(ellipse at 50% 43%, #24344d 0%, #101b2d 47%, #070e1a 100%);
|
|
1239
|
+
}
|
|
1240
|
+
body::before {
|
|
1241
|
+
content: '';
|
|
1242
|
+
position: absolute;
|
|
1243
|
+
width: 82%;
|
|
1244
|
+
height: 62%;
|
|
1245
|
+
border-radius: 50%;
|
|
1246
|
+
background: ${accent};
|
|
1247
|
+
opacity: 0.11;
|
|
1248
|
+
filter: blur(90px);
|
|
1249
|
+
}
|
|
1250
|
+
.device {
|
|
1251
|
+
position: relative;
|
|
1252
|
+
z-index: 1;
|
|
1253
|
+
width: ${screenWidth + 48}px;
|
|
1254
|
+
padding: 34px 18px 20px;
|
|
1255
|
+
border: 6px solid #8d99ac;
|
|
1256
|
+
border-radius: 48px;
|
|
1257
|
+
background: linear-gradient(135deg, #404c60, #111925 35%, #070c15 72%, #59657a);
|
|
1258
|
+
box-shadow: 0 38px 85px rgba(0, 0, 0, 0.58), 0 0 0 2px rgba(255, 255, 255, 0.19) inset;
|
|
1259
|
+
}
|
|
1260
|
+
.screen {
|
|
1261
|
+
width: ${screenWidth}px;
|
|
1262
|
+
background: #fff;
|
|
1263
|
+
box-shadow: 0 0 0 2px #03060c;
|
|
1264
|
+
}
|
|
1265
|
+
.screen img { display: block; width: 100%; height: auto; }
|
|
1266
|
+
.camera {
|
|
1267
|
+
position: absolute;
|
|
1268
|
+
top: 11px;
|
|
1269
|
+
left: 50%;
|
|
1270
|
+
width: 12px;
|
|
1271
|
+
height: 12px;
|
|
1272
|
+
transform: translateX(-50%);
|
|
1273
|
+
border-radius: 50%;
|
|
1274
|
+
background: radial-gradient(circle at 32% 30%, #547496, #070b12 48%, #000 80%);
|
|
1275
|
+
box-shadow: 0 0 0 3px #1b2534;
|
|
1276
|
+
}
|
|
1277
|
+
.side-button {
|
|
1278
|
+
position: absolute;
|
|
1279
|
+
top: 19%;
|
|
1280
|
+
right: -10px;
|
|
1281
|
+
width: 4px;
|
|
1282
|
+
height: 66px;
|
|
1283
|
+
border-radius: 0 3px 3px 0;
|
|
1284
|
+
background: #79869a;
|
|
1285
|
+
}
|
|
1286
|
+
</style>
|
|
1287
|
+
</head>
|
|
1288
|
+
<body>
|
|
1289
|
+
<main class="device">
|
|
1290
|
+
<div class="camera"></div>
|
|
1291
|
+
<div class="screen"><img src="${screenshotDataUrl}" /></div>
|
|
1292
|
+
<div class="side-button"></div>
|
|
1293
|
+
</main>
|
|
1294
|
+
</body>
|
|
1295
|
+
</html>`;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
function createDeviceFrameHtml(profile, screenshotBuffer, imageWidth = profile.width, imageHeight = profile.height, imageFormat = 'png') {
|
|
1299
|
+
const screenshotDataUrl = `data:image/${imageFormat === 'jpeg' ? 'jpeg' : 'png'};base64,${screenshotBuffer.toString('base64')}`;
|
|
1300
|
+
const aspectRatio = `${imageWidth} / ${imageHeight}`;
|
|
1160
1301
|
|
|
1161
1302
|
return `<!doctype html>
|
|
1162
1303
|
<html>
|
|
@@ -1200,20 +1341,21 @@ function createDeviceFrameHtml(profile, screenshotBuffer) {
|
|
|
1200
1341
|
.screen {
|
|
1201
1342
|
position: relative;
|
|
1202
1343
|
width: 100%;
|
|
1203
|
-
height:
|
|
1344
|
+
height: 87%;
|
|
1204
1345
|
overflow: hidden;
|
|
1205
|
-
background: #
|
|
1346
|
+
background: #f5f7fc;
|
|
1206
1347
|
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.95) inset;
|
|
1207
1348
|
}
|
|
1208
1349
|
.screen img {
|
|
1209
1350
|
display: block;
|
|
1210
1351
|
width: 100%;
|
|
1211
1352
|
height: 100%;
|
|
1212
|
-
object-fit:
|
|
1353
|
+
object-fit: contain;
|
|
1354
|
+
background: #f5f7fc;
|
|
1213
1355
|
}
|
|
1214
1356
|
.device.android-phone,
|
|
1215
1357
|
.device.iphone {
|
|
1216
|
-
height:
|
|
1358
|
+
height: 100%;
|
|
1217
1359
|
padding: 1.25%;
|
|
1218
1360
|
border-radius: clamp(24px, 8vw, 70px);
|
|
1219
1361
|
}
|
|
@@ -2040,9 +2182,15 @@ async function captureFramedScreenshot(
|
|
|
2040
2182
|
return false;
|
|
2041
2183
|
}
|
|
2042
2184
|
|
|
2043
|
-
const
|
|
2044
|
-
|
|
2045
|
-
|
|
2185
|
+
const originalImageMetadata = profile.storeRequirements
|
|
2186
|
+
? await sharp(screenshotBuffer).metadata()
|
|
2187
|
+
: null;
|
|
2188
|
+
const frameDimensions = originalImageMetadata?.width && originalImageMetadata?.height
|
|
2189
|
+
? { width: originalImageMetadata.width, height: originalImageMetadata.height }
|
|
2190
|
+
: null;
|
|
2191
|
+
const hasCustomFrameSize = Boolean(frameDimensions || (profile.framedWidth && profile.framedHeight));
|
|
2192
|
+
const frameWidth = frameDimensions?.width || profile.framedWidth || profile.width;
|
|
2193
|
+
const frameHeight = frameDimensions?.height || profile.framedHeight || profile.height;
|
|
2046
2194
|
const frameScaleFactor = hasCustomFrameSize ? 1 : profile.deviceScaleFactor;
|
|
2047
2195
|
|
|
2048
2196
|
await client.send('Emulation.setDeviceMetricsOverride', {
|
|
@@ -2059,7 +2207,12 @@ async function captureFramedScreenshot(
|
|
|
2059
2207
|
const frameTree = await client.send('Page.getFrameTree');
|
|
2060
2208
|
await client.send('Page.setDocumentContent', {
|
|
2061
2209
|
frameId: frameTree.frameTree.frame.id,
|
|
2062
|
-
html:
|
|
2210
|
+
html: profile.name === 'vivo-store' || profile.name === 'oppo-store'
|
|
2211
|
+
? createStoreDeviceFrameHtml(
|
|
2212
|
+
profile, screenshotBuffer, originalImageMetadata.width, originalImageMetadata.height,
|
|
2213
|
+
frameWidth, frameHeight, originalImageMetadata.format,
|
|
2214
|
+
)
|
|
2215
|
+
: createDeviceFrameHtml(profile, screenshotBuffer, frameWidth, frameHeight),
|
|
2063
2216
|
});
|
|
2064
2217
|
await client.send('Runtime.evaluate', {
|
|
2065
2218
|
expression: `new Promise((resolve) => {
|
|
@@ -2076,21 +2229,52 @@ async function captureFramedScreenshot(
|
|
|
2076
2229
|
});
|
|
2077
2230
|
|
|
2078
2231
|
const framedScreenshot = await client.send('Page.captureScreenshot', {
|
|
2079
|
-
format: 'png',
|
|
2232
|
+
format: profile.outputFormat || 'png',
|
|
2233
|
+
quality: profile.outputFormat === 'jpeg' ? 90 : undefined,
|
|
2080
2234
|
fromSurface: true,
|
|
2081
2235
|
captureBeyondViewport: false,
|
|
2082
2236
|
});
|
|
2083
2237
|
|
|
2238
|
+
const framedScreenshotBuffer = Buffer.from(framedScreenshot.data, 'base64');
|
|
2239
|
+
if (!frameDimensions) {
|
|
2240
|
+
validateStoreImage(profile, frameWidth, frameHeight, framedScreenshotBuffer);
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2084
2243
|
fs.mkdirSync(framedProfileDirectory, { recursive: true });
|
|
2085
2244
|
fs.writeFileSync(
|
|
2086
2245
|
framedScreenshotPath,
|
|
2087
|
-
|
|
2088
|
-
{ flag: 'wx' },
|
|
2246
|
+
framedScreenshotBuffer,
|
|
2247
|
+
{ flag: skipExisting ? 'wx' : 'w' },
|
|
2089
2248
|
);
|
|
2090
2249
|
console.log(`Saved framed ${profile.label}: ${framedScreenshotPath}`);
|
|
2091
2250
|
return true;
|
|
2092
2251
|
}
|
|
2093
2252
|
|
|
2253
|
+
function validateStoreImage(profile, width, height, imageBuffer) {
|
|
2254
|
+
if (!profile.storeRequirements) return;
|
|
2255
|
+
|
|
2256
|
+
const {
|
|
2257
|
+
maxFileSizeBytes, minPixels, maxPixels, minAspectRatio, maxAspectRatio,
|
|
2258
|
+
} = profile.storeRequirements;
|
|
2259
|
+
const aspectRatio = width / height;
|
|
2260
|
+
|
|
2261
|
+
if (
|
|
2262
|
+
width < minPixels || height < minPixels
|
|
2263
|
+
|| width > maxPixels || height > maxPixels
|
|
2264
|
+
|| aspectRatio < minAspectRatio || aspectRatio > maxAspectRatio
|
|
2265
|
+
) {
|
|
2266
|
+
throw new Error(
|
|
2267
|
+
`${profile.label} must be between ${minPixels}px and ${maxPixels}px with aspect ratio <= ${maxAspectRatio}:1; received ${width}x${height}.`,
|
|
2268
|
+
);
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
if (imageBuffer.length > maxFileSizeBytes) {
|
|
2272
|
+
throw new Error(
|
|
2273
|
+
`${profile.label} exceeds ${maxFileSizeBytes / (1024 * 1024)} MB: ${Math.ceil(imageBuffer.length / (1024 * 1024))} MB.`,
|
|
2274
|
+
);
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2094
2278
|
async function captureProfile(client, snapshot, profile, screenshotFileName) {
|
|
2095
2279
|
await client.send('Emulation.setDeviceMetricsOverride', {
|
|
2096
2280
|
width: profile.width,
|
|
@@ -2118,7 +2302,8 @@ async function captureProfile(client, snapshot, profile, screenshotFileName) {
|
|
|
2118
2302
|
}
|
|
2119
2303
|
|
|
2120
2304
|
const screenshot = await client.send('Page.captureScreenshot', {
|
|
2121
|
-
format: 'png',
|
|
2305
|
+
format: profile.outputFormat || 'png',
|
|
2306
|
+
quality: profile.outputFormat === 'jpeg' ? 90 : undefined,
|
|
2122
2307
|
fromSurface: true,
|
|
2123
2308
|
captureBeyondViewport: false,
|
|
2124
2309
|
});
|
|
@@ -2126,6 +2311,12 @@ async function captureProfile(client, snapshot, profile, screenshotFileName) {
|
|
|
2126
2311
|
const profileDirectory = path.join(screenshotDirectory, profile.outputDirectory);
|
|
2127
2312
|
const screenshotPath = path.join(profileDirectory, screenshotFileName);
|
|
2128
2313
|
const screenshotBuffer = Buffer.from(screenshot.data, 'base64');
|
|
2314
|
+
validateStoreImage(
|
|
2315
|
+
profile,
|
|
2316
|
+
profile.width * profile.deviceScaleFactor,
|
|
2317
|
+
profile.height * profile.deviceScaleFactor,
|
|
2318
|
+
screenshotBuffer,
|
|
2319
|
+
);
|
|
2129
2320
|
|
|
2130
2321
|
fs.mkdirSync(profileDirectory, { recursive: true });
|
|
2131
2322
|
fs.writeFileSync(screenshotPath, screenshotBuffer, { flag: 'wx' });
|
|
@@ -2145,6 +2336,22 @@ async function captureScreenshotSet(
|
|
|
2145
2336
|
selectedProfiles,
|
|
2146
2337
|
fileNameInput,
|
|
2147
2338
|
) {
|
|
2339
|
+
const storeProfile = selectedProfiles.find((profile) => profile.storeRequirements);
|
|
2340
|
+
if (storeProfile) {
|
|
2341
|
+
const profileDirectory = path.join(screenshotDirectory, storeProfile.outputDirectory);
|
|
2342
|
+
const existingImageCount = fs.existsSync(profileDirectory)
|
|
2343
|
+
? fs.readdirSync(profileDirectory, { withFileTypes: true })
|
|
2344
|
+
.filter((entry) => entry.isFile() && /\.(png|jpe?g|webp)$/i.test(entry.name))
|
|
2345
|
+
.length
|
|
2346
|
+
: 0;
|
|
2347
|
+
|
|
2348
|
+
if (existingImageCount >= storeProfile.storeRequirements.maxImages) {
|
|
2349
|
+
throw new Error(
|
|
2350
|
+
`${storeProfile.label} already contains ${existingImageCount} images; the store allows a maximum of ${storeProfile.storeRequirements.maxImages}.`,
|
|
2351
|
+
);
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2148
2355
|
console.log('Capturing the current rendered page and resolved native data...');
|
|
2149
2356
|
const snapshot = await capturePageSnapshot(deviceClient);
|
|
2150
2357
|
const screenshotFileName = getScreenshotFileName(fileNameInput, selectedProfiles);
|
|
@@ -2213,6 +2420,148 @@ async function frameExistingScreenshots(selectedProfiles) {
|
|
|
2213
2420
|
}
|
|
2214
2421
|
}
|
|
2215
2422
|
|
|
2423
|
+
async function createVivoAssetsFromExistingScreenshots() {
|
|
2424
|
+
const vivoProfile = deviceProfiles.find((profile) => profile.name === 'vivo-store');
|
|
2425
|
+
const sourceDirectory = path.join(screenshotDirectory, 'Android');
|
|
2426
|
+
const outputDirectory = path.join(screenshotDirectory, vivoProfile.outputDirectory);
|
|
2427
|
+
const sourceFiles = fs.existsSync(sourceDirectory)
|
|
2428
|
+
? fs.readdirSync(sourceDirectory, { withFileTypes: true })
|
|
2429
|
+
.filter((entry) => entry.isFile() && /\.png$/i.test(entry.name))
|
|
2430
|
+
.map((entry) => entry.name)
|
|
2431
|
+
.sort((first, second) => first.localeCompare(second, undefined, { numeric: true }))
|
|
2432
|
+
: [];
|
|
2433
|
+
|
|
2434
|
+
if (!sourceFiles.length) {
|
|
2435
|
+
throw new Error(`No existing PNG screenshots found in ${sourceDirectory}.`);
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
const { maxImages } = vivoProfile.storeRequirements;
|
|
2439
|
+
const selectedSourceFiles = sourceFiles.slice(0, maxImages);
|
|
2440
|
+
if (sourceFiles.length > maxImages) {
|
|
2441
|
+
console.warn(
|
|
2442
|
+
`Vivo Store allows a maximum of ${maxImages} images; using the first ${maxImages} of ${sourceFiles.length} existing Android screenshots.`,
|
|
2443
|
+
);
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
let replayBrowser;
|
|
2447
|
+
let replayClient;
|
|
2448
|
+
let createdCount = 0;
|
|
2449
|
+
|
|
2450
|
+
try {
|
|
2451
|
+
replayBrowser = await launchReplayBrowser();
|
|
2452
|
+
replayClient = await createReplayPage(replayBrowser);
|
|
2453
|
+
fs.mkdirSync(outputDirectory, { recursive: true });
|
|
2454
|
+
|
|
2455
|
+
for (const screenshotFileName of selectedSourceFiles) {
|
|
2456
|
+
const sourcePath = path.join(sourceDirectory, screenshotFileName);
|
|
2457
|
+
const outputPath = path.join(outputDirectory, screenshotFileName);
|
|
2458
|
+
const sourceBuffer = fs.readFileSync(sourcePath);
|
|
2459
|
+
const vivoBuffer = await sharp(sourceBuffer)
|
|
2460
|
+
.resize(vivoProfile.width * vivoProfile.deviceScaleFactor, vivoProfile.height * vivoProfile.deviceScaleFactor, {
|
|
2461
|
+
fit: 'contain',
|
|
2462
|
+
background: '#f5f7fc',
|
|
2463
|
+
})
|
|
2464
|
+
.png()
|
|
2465
|
+
.toBuffer();
|
|
2466
|
+
|
|
2467
|
+
validateStoreImage(
|
|
2468
|
+
vivoProfile,
|
|
2469
|
+
vivoProfile.width * vivoProfile.deviceScaleFactor,
|
|
2470
|
+
vivoProfile.height * vivoProfile.deviceScaleFactor,
|
|
2471
|
+
vivoBuffer,
|
|
2472
|
+
);
|
|
2473
|
+
|
|
2474
|
+
fs.writeFileSync(outputPath, vivoBuffer);
|
|
2475
|
+
createdCount += 1;
|
|
2476
|
+
console.log(`Saved Vivo frameless screenshot: ${outputPath}`);
|
|
2477
|
+
|
|
2478
|
+
await captureFramedScreenshot(
|
|
2479
|
+
replayClient,
|
|
2480
|
+
sourceBuffer,
|
|
2481
|
+
vivoProfile,
|
|
2482
|
+
screenshotFileName,
|
|
2483
|
+
false,
|
|
2484
|
+
);
|
|
2485
|
+
}
|
|
2486
|
+
} finally {
|
|
2487
|
+
if (replayClient) replayClient.close();
|
|
2488
|
+
if (replayBrowser) await closeReplayBrowser(replayBrowser);
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
console.log(`Vivo assets created from existing screenshots: ${createdCount} frameless image(s).`);
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
async function createOppoAssetsFromExistingScreenshots() {
|
|
2495
|
+
const oppoProfile = deviceProfiles.find((profile) => profile.name === 'oppo-store');
|
|
2496
|
+
const sourceDirectory = path.join(screenshotDirectory, 'Android');
|
|
2497
|
+
const outputDirectory = path.join(screenshotDirectory, oppoProfile.outputDirectory);
|
|
2498
|
+
const sourceFiles = fs.existsSync(sourceDirectory)
|
|
2499
|
+
? fs.readdirSync(sourceDirectory, { withFileTypes: true })
|
|
2500
|
+
.filter((entry) => entry.isFile() && /\.png$/i.test(entry.name))
|
|
2501
|
+
.map((entry) => entry.name)
|
|
2502
|
+
.sort((first, second) => first.localeCompare(second, undefined, { numeric: true }))
|
|
2503
|
+
: [];
|
|
2504
|
+
|
|
2505
|
+
if (!sourceFiles.length) {
|
|
2506
|
+
throw new Error(`No existing PNG screenshots found in ${sourceDirectory}.`);
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
const { maxImages } = oppoProfile.storeRequirements;
|
|
2510
|
+
const selectedSourceFiles = sourceFiles.slice(0, maxImages);
|
|
2511
|
+
if (sourceFiles.length > maxImages) {
|
|
2512
|
+
console.warn(
|
|
2513
|
+
`Oppo Store allows a maximum of ${maxImages} images; using the first ${maxImages} of ${sourceFiles.length} existing Android screenshots.`,
|
|
2514
|
+
);
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
let replayBrowser;
|
|
2518
|
+
let replayClient;
|
|
2519
|
+
let createdCount = 0;
|
|
2520
|
+
|
|
2521
|
+
try {
|
|
2522
|
+
replayBrowser = await launchReplayBrowser();
|
|
2523
|
+
replayClient = await createReplayPage(replayBrowser);
|
|
2524
|
+
fs.mkdirSync(outputDirectory, { recursive: true });
|
|
2525
|
+
|
|
2526
|
+
for (const sourceFileName of selectedSourceFiles) {
|
|
2527
|
+
const outputFileName = `${path.basename(sourceFileName, path.extname(sourceFileName))}.jpg`;
|
|
2528
|
+
const sourceBuffer = fs.readFileSync(path.join(sourceDirectory, sourceFileName));
|
|
2529
|
+
const oppoBuffer = await sharp(sourceBuffer)
|
|
2530
|
+
.resize(oppoProfile.width * oppoProfile.deviceScaleFactor, oppoProfile.height * oppoProfile.deviceScaleFactor, {
|
|
2531
|
+
fit: 'contain',
|
|
2532
|
+
background: '#f5f7fc',
|
|
2533
|
+
})
|
|
2534
|
+
.jpeg({ quality: 90 })
|
|
2535
|
+
.toBuffer();
|
|
2536
|
+
const outputPath = path.join(outputDirectory, outputFileName);
|
|
2537
|
+
|
|
2538
|
+
validateStoreImage(
|
|
2539
|
+
oppoProfile,
|
|
2540
|
+
oppoProfile.width * oppoProfile.deviceScaleFactor,
|
|
2541
|
+
oppoProfile.height * oppoProfile.deviceScaleFactor,
|
|
2542
|
+
oppoBuffer,
|
|
2543
|
+
);
|
|
2544
|
+
|
|
2545
|
+
fs.writeFileSync(outputPath, oppoBuffer);
|
|
2546
|
+
createdCount += 1;
|
|
2547
|
+
console.log(`Saved Oppo frameless screenshot: ${outputPath}`);
|
|
2548
|
+
|
|
2549
|
+
await captureFramedScreenshot(
|
|
2550
|
+
replayClient,
|
|
2551
|
+
sourceBuffer,
|
|
2552
|
+
oppoProfile,
|
|
2553
|
+
outputFileName,
|
|
2554
|
+
false,
|
|
2555
|
+
);
|
|
2556
|
+
}
|
|
2557
|
+
} finally {
|
|
2558
|
+
if (replayClient) replayClient.close();
|
|
2559
|
+
if (replayBrowser) await closeReplayBrowser(replayBrowser);
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
console.log(`Oppo assets created from existing screenshots: ${createdCount} frameless image(s).`);
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2216
2565
|
async function runInteractiveCaptureLoop(
|
|
2217
2566
|
deviceClient,
|
|
2218
2567
|
replayClient,
|
|
@@ -2254,6 +2603,8 @@ async function getInteractiveOptions(options) {
|
|
|
2254
2603
|
|| options.dryRun
|
|
2255
2604
|
|| options.amazonAssets
|
|
2256
2605
|
|| options.samsungAssets
|
|
2606
|
+
|| options.vivoExisting
|
|
2607
|
+
|| options.oppoExisting
|
|
2257
2608
|
) {
|
|
2258
2609
|
return options;
|
|
2259
2610
|
}
|
|
@@ -2332,6 +2683,16 @@ async function main() {
|
|
|
2332
2683
|
return;
|
|
2333
2684
|
}
|
|
2334
2685
|
|
|
2686
|
+
if (options.vivoExisting) {
|
|
2687
|
+
await createVivoAssetsFromExistingScreenshots();
|
|
2688
|
+
return;
|
|
2689
|
+
}
|
|
2690
|
+
|
|
2691
|
+
if (options.oppoExisting) {
|
|
2692
|
+
await createOppoAssetsFromExistingScreenshots();
|
|
2693
|
+
return;
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2335
2696
|
if (!options.dryRun) {
|
|
2336
2697
|
await createMissingStoreAssets();
|
|
2337
2698
|
}
|