pabal-store-api-mcp 1.3.10 → 1.3.13
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/dist/src/core/services/app-store-service.d.ts +7 -1
- package/dist/src/core/services/app-store-service.js +26 -5
- package/dist/src/core/services/google-play-service.d.ts +11 -1
- package/dist/src/core/services/google-play-service.js +66 -10
- package/dist/src/index.js +10 -0
- package/dist/src/packages/common/errors/error-codes.d.ts +2 -0
- package/dist/src/packages/common/errors/error-codes.js +2 -0
- package/dist/src/packages/stores/app-store/client.d.ts +2 -0
- package/dist/src/packages/stores/app-store/client.js +14 -5
- package/dist/src/packages/stores/play-store/client.d.ts +3 -0
- package/dist/src/packages/stores/play-store/client.js +143 -137
- package/dist/src/packages/stores/play-store/types.d.ts +1 -0
- package/dist/src/tools/aso/push.d.ts +2 -0
- package/dist/src/tools/aso/push.js +10 -1
- package/package.json +1 -1
|
@@ -7,6 +7,10 @@ interface AppStoreAppInfo {
|
|
|
7
7
|
name?: string;
|
|
8
8
|
supportedLocales?: string[];
|
|
9
9
|
}
|
|
10
|
+
export declare function resolveAppStoreLocales(allLocales: string[], requestedLocales?: string[]): {
|
|
11
|
+
localesToPush: string[];
|
|
12
|
+
missingLocales: string[];
|
|
13
|
+
};
|
|
10
14
|
/**
|
|
11
15
|
* App Store-facing service layer that wraps client creation and common operations.
|
|
12
16
|
* Keeps MCP tools independent from client factories and SDK details.
|
|
@@ -26,12 +30,14 @@ export declare class AppStoreService {
|
|
|
26
30
|
updateReleaseNotes(bundleId: string, releaseNotes: Record<string, string>, versionId?: string, supportedLocales?: string[]): Promise<ServiceResult<UpdatedReleaseNotesResult>>;
|
|
27
31
|
pullReleaseNotes(bundleId: string): Promise<ServiceResult<AppStoreReleaseNote[]>>;
|
|
28
32
|
createVersion(bundleId: string, versionString: string, autoIncrement?: boolean): Promise<ServiceResult<CreatedAppStoreVersion>>;
|
|
29
|
-
pushAsoData({ config, bundleId, localAsoData, appStoreDataPath, uploadImages, slug, }: {
|
|
33
|
+
pushAsoData({ config, bundleId, localAsoData, appStoreDataPath, uploadImages, locales, imageUploadTimeoutMs, slug, }: {
|
|
30
34
|
config: EnvConfig;
|
|
31
35
|
bundleId?: string;
|
|
32
36
|
localAsoData: AsoData;
|
|
33
37
|
appStoreDataPath: string;
|
|
34
38
|
uploadImages?: boolean;
|
|
39
|
+
locales?: string[];
|
|
40
|
+
imageUploadTimeoutMs?: number;
|
|
35
41
|
slug?: string;
|
|
36
42
|
}): Promise<PushAsoResult>;
|
|
37
43
|
verifyAuth(expirationSeconds?: number): Promise<VerifyAuthResult<{
|
|
@@ -6,6 +6,16 @@ import { verifyAppStoreAuth } from "../../packages/stores/app-store/verify-auth.
|
|
|
6
6
|
import { createAppStoreClient } from "../../core/clients/app-store-factory.js";
|
|
7
7
|
import { parseAppStoreScreenshots, hasScreenshots, APP_STORE_DEVICE_TYPES, } from "../../core/helpers/screenshot-helpers.js";
|
|
8
8
|
import { checkPushPrerequisites, serviceFailure, toServiceResult, updateRegisteredLocales, } from "./service-helpers.js";
|
|
9
|
+
export function resolveAppStoreLocales(allLocales, requestedLocales) {
|
|
10
|
+
if (!requestedLocales?.length) {
|
|
11
|
+
return { localesToPush: allLocales, missingLocales: [] };
|
|
12
|
+
}
|
|
13
|
+
const requested = new Set(requestedLocales);
|
|
14
|
+
return {
|
|
15
|
+
localesToPush: allLocales.filter((locale) => requested.has(locale)),
|
|
16
|
+
missingLocales: requestedLocales.filter((locale) => !allLocales.includes(locale)),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
9
19
|
/**
|
|
10
20
|
* App Store-facing service layer that wraps client creation and common operations.
|
|
11
21
|
* Keeps MCP tools independent from client factories and SDK details.
|
|
@@ -163,7 +173,7 @@ export class AppStoreService {
|
|
|
163
173
|
return serviceFailure(AppError.wrap(error, HTTP_STATUS.INTERNAL_SERVER_ERROR, ERROR_CODES.APP_STORE_CREATE_VERSION_FAILED, "Failed to create App Store version"));
|
|
164
174
|
}
|
|
165
175
|
}
|
|
166
|
-
async pushAsoData({ config, bundleId, localAsoData, appStoreDataPath, uploadImages = false, slug, }) {
|
|
176
|
+
async pushAsoData({ config, bundleId, localAsoData, appStoreDataPath, uploadImages = false, locales, imageUploadTimeoutMs, slug, }) {
|
|
167
177
|
const skip = checkPushPrerequisites({
|
|
168
178
|
storeLabel: "App Store",
|
|
169
179
|
configured: Boolean(config.appStore),
|
|
@@ -181,9 +191,20 @@ export class AppStoreService {
|
|
|
181
191
|
console.error(`[MCP] Bundle ID: ${bundleId}`);
|
|
182
192
|
try {
|
|
183
193
|
// Push locale data (supportUrl/marketingUrl already set by prepareAsoDataForPush)
|
|
184
|
-
const
|
|
194
|
+
const allLocales = Object.keys(appStoreData.locales);
|
|
195
|
+
const { localesToPush, missingLocales } = resolveAppStoreLocales(allLocales, locales);
|
|
196
|
+
if (missingLocales.length) {
|
|
197
|
+
console.error(`[AppStore] ⚠️ Requested locale(s) not found in local ASO data: ${missingLocales.join(", ")}`);
|
|
198
|
+
}
|
|
199
|
+
if (localesToPush.length === 0) {
|
|
200
|
+
return {
|
|
201
|
+
success: false,
|
|
202
|
+
error: AppError.validation(ERROR_CODES.APP_STORE_ASO_DATA_EMPTY, "No matching App Store locales found to push"),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
185
205
|
const failedFieldsList = [];
|
|
186
|
-
for (const
|
|
206
|
+
for (const locale of localesToPush) {
|
|
207
|
+
const localeData = appStoreData.locales[locale];
|
|
187
208
|
console.error(`[AppStore] 📤 Pushing ${locale}...`);
|
|
188
209
|
const localeResult = await client.pushAsoData(localeData);
|
|
189
210
|
if (localeResult.failedFields && localeResult.failedFields.length > 0) {
|
|
@@ -279,6 +300,7 @@ export class AppStoreService {
|
|
|
279
300
|
const uploadResult = await client.uploadScreenshotsForLocale({
|
|
280
301
|
locale,
|
|
281
302
|
screenshots: screenshotsToUpload,
|
|
303
|
+
imageUploadTimeoutMs,
|
|
282
304
|
});
|
|
283
305
|
if (uploadResult.failed > 0) {
|
|
284
306
|
throw new Error(`Screenshot upload reported ${uploadResult.failed} failed files`);
|
|
@@ -335,7 +357,6 @@ export class AppStoreService {
|
|
|
335
357
|
const version = await client.createNewVersionWithAutoIncrement();
|
|
336
358
|
const versionId = version.id;
|
|
337
359
|
const versionString = version.attributes?.versionString ?? "";
|
|
338
|
-
const locales = Object.keys(appStoreData.locales);
|
|
339
360
|
console.error(`[AppStore] ✅ New version ${versionString} created.`);
|
|
340
361
|
return {
|
|
341
362
|
success: false,
|
|
@@ -344,7 +365,7 @@ export class AppStoreService {
|
|
|
344
365
|
versionInfo: {
|
|
345
366
|
versionId,
|
|
346
367
|
versionString,
|
|
347
|
-
locales,
|
|
368
|
+
locales: Object.keys(appStoreData.locales),
|
|
348
369
|
},
|
|
349
370
|
};
|
|
350
371
|
}
|
|
@@ -6,6 +6,14 @@ interface GooglePlayAppInfo {
|
|
|
6
6
|
name?: string;
|
|
7
7
|
supportedLocales?: string[];
|
|
8
8
|
}
|
|
9
|
+
export declare function resolveGooglePlayLocales(allLocales: string[], requestedLocales?: string[]): {
|
|
10
|
+
localesToPush: string[];
|
|
11
|
+
missingLocales: string[];
|
|
12
|
+
};
|
|
13
|
+
export declare function shouldPushGooglePlayAppDetails({ hasContactDetails, requestedLocales, }: {
|
|
14
|
+
hasContactDetails: boolean;
|
|
15
|
+
requestedLocales?: string[];
|
|
16
|
+
}): boolean;
|
|
9
17
|
/**
|
|
10
18
|
* Google Play-facing service layer that wraps client creation and common operations.
|
|
11
19
|
* Keeps MCP tools independent from client factories and SDK details.
|
|
@@ -21,12 +29,14 @@ export declare class GooglePlayService {
|
|
|
21
29
|
updateReleaseNotes(packageName: string, releaseNotes: Record<string, string>, track?: string, supportedLocales?: string[]): Promise<ServiceResult<UpdatedReleaseNotesResult>>;
|
|
22
30
|
pullReleaseNotes(packageName: string): Promise<ServiceResult<GooglePlayReleaseNote[]>>;
|
|
23
31
|
createVersion(packageName: string, versionString: string, versionCodes: number[]): Promise<ServiceResult<CreatedGooglePlayVersion>>;
|
|
24
|
-
pushAsoData({ config, packageName, localAsoData, googlePlayDataPath, uploadImages, slug, }: {
|
|
32
|
+
pushAsoData({ config, packageName, localAsoData, googlePlayDataPath, uploadImages, locales, imageUploadTimeoutMs, slug, }: {
|
|
25
33
|
config: EnvConfig;
|
|
26
34
|
packageName?: string;
|
|
27
35
|
localAsoData: AsoData;
|
|
28
36
|
googlePlayDataPath: string;
|
|
29
37
|
uploadImages?: boolean;
|
|
38
|
+
locales?: string[];
|
|
39
|
+
imageUploadTimeoutMs?: number;
|
|
30
40
|
slug?: string;
|
|
31
41
|
}): Promise<PushAsoResult>;
|
|
32
42
|
verifyAuth(): Promise<VerifyAuthResult<{
|
|
@@ -6,6 +6,20 @@ import { verifyPlayStoreAuth } from "../../packages/stores/play-store/verify-aut
|
|
|
6
6
|
import { createGooglePlayClient } from "../../core/clients/google-play-factory.js";
|
|
7
7
|
import { parseGooglePlayScreenshots, hasScreenshots, } from "../../core/helpers/screenshot-helpers.js";
|
|
8
8
|
import { checkPushPrerequisites, serviceFailure, toServiceResult, updateRegisteredLocales, } from "./service-helpers.js";
|
|
9
|
+
const GOOGLE_PLAY_SCREENSHOT_LOCALE_BATCH_SIZE = 5;
|
|
10
|
+
export function resolveGooglePlayLocales(allLocales, requestedLocales) {
|
|
11
|
+
if (!requestedLocales?.length) {
|
|
12
|
+
return { localesToPush: allLocales, missingLocales: [] };
|
|
13
|
+
}
|
|
14
|
+
const requested = new Set(requestedLocales);
|
|
15
|
+
return {
|
|
16
|
+
localesToPush: allLocales.filter((locale) => requested.has(locale)),
|
|
17
|
+
missingLocales: requestedLocales.filter((locale) => !allLocales.includes(locale)),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function shouldPushGooglePlayAppDetails({ hasContactDetails, requestedLocales, }) {
|
|
21
|
+
return hasContactDetails && !requestedLocales?.length;
|
|
22
|
+
}
|
|
9
23
|
/**
|
|
10
24
|
* Google Play-facing service layer that wraps client creation and common operations.
|
|
11
25
|
* Keeps MCP tools independent from client factories and SDK details.
|
|
@@ -148,7 +162,7 @@ export class GooglePlayService {
|
|
|
148
162
|
return serviceFailure(AppError.wrap(error, HTTP_STATUS.INTERNAL_SERVER_ERROR, ERROR_CODES.GOOGLE_PLAY_CREATE_VERSION_FAILED, "Failed to create Google Play version"));
|
|
149
163
|
}
|
|
150
164
|
}
|
|
151
|
-
async pushAsoData({ config, packageName, localAsoData, googlePlayDataPath, uploadImages = false, slug, }) {
|
|
165
|
+
async pushAsoData({ config, packageName, localAsoData, googlePlayDataPath, uploadImages = false, locales, imageUploadTimeoutMs, slug, }) {
|
|
152
166
|
const skip = checkPushPrerequisites({
|
|
153
167
|
storeLabel: "Google Play",
|
|
154
168
|
configured: Boolean(config.playStore),
|
|
@@ -165,14 +179,35 @@ export class GooglePlayService {
|
|
|
165
179
|
console.error(`[MCP] 📤 Pushing to Google Play...`);
|
|
166
180
|
console.error(`[MCP] Package: ${packageName}`);
|
|
167
181
|
try {
|
|
168
|
-
const
|
|
182
|
+
const allLocales = Object.keys(googlePlayData.locales);
|
|
183
|
+
const { localesToPush, missingLocales } = resolveGooglePlayLocales(allLocales, locales);
|
|
184
|
+
if (missingLocales.length) {
|
|
185
|
+
console.error(`[GooglePlay] ⚠️ Requested locale(s) not found in local ASO data: ${missingLocales.join(", ")}`);
|
|
186
|
+
}
|
|
187
|
+
if (localesToPush.length === 0) {
|
|
188
|
+
return {
|
|
189
|
+
success: false,
|
|
190
|
+
error: AppError.validation(ERROR_CODES.GOOGLE_PLAY_ASO_DATA_EMPTY, "No matching Google Play locales found to push"),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
169
193
|
for (const locale of localesToPush) {
|
|
170
194
|
console.error(`[GooglePlay] 📤 Preparing locale: ${locale}`);
|
|
171
195
|
}
|
|
172
196
|
// Push locale data as-is from aso-data.json
|
|
173
|
-
await client.pushMultilingualAsoData(
|
|
174
|
-
|
|
175
|
-
|
|
197
|
+
await client.pushMultilingualAsoData({
|
|
198
|
+
...googlePlayData,
|
|
199
|
+
locales: Object.fromEntries(localesToPush.map((locale) => [
|
|
200
|
+
locale,
|
|
201
|
+
googlePlayData.locales[locale],
|
|
202
|
+
])),
|
|
203
|
+
});
|
|
204
|
+
// Push app-level contact information once for full pushes. Partial locale
|
|
205
|
+
// pushes are commonly batched, and repeating details edits can invalidate
|
|
206
|
+
// otherwise-successful listing commits on Google Play.
|
|
207
|
+
if (shouldPushGooglePlayAppDetails({
|
|
208
|
+
hasContactDetails: Boolean(googlePlayData.contactEmail || googlePlayData.contactWebsite),
|
|
209
|
+
requestedLocales: locales,
|
|
210
|
+
})) {
|
|
176
211
|
console.error(`[GooglePlay] 📤 Pushing app details...`);
|
|
177
212
|
await client.pushAppDetails({
|
|
178
213
|
contactEmail: googlePlayData.contactEmail,
|
|
@@ -180,6 +215,9 @@ export class GooglePlayService {
|
|
|
180
215
|
});
|
|
181
216
|
console.error(`[GooglePlay] ✅ App details uploaded successfully`);
|
|
182
217
|
}
|
|
218
|
+
else if (locales?.length) {
|
|
219
|
+
console.error(`[GooglePlay] ⏭️ Skipping app details for partial locale push`);
|
|
220
|
+
}
|
|
183
221
|
// Upload screenshots if enabled
|
|
184
222
|
if (uploadImages && slug) {
|
|
185
223
|
console.error(`[GooglePlay] 📤 Uploading screenshots...`);
|
|
@@ -188,6 +226,7 @@ export class GooglePlayService {
|
|
|
188
226
|
const uploadedLocales = [];
|
|
189
227
|
const skippedLocales = [];
|
|
190
228
|
const failedLocales = [];
|
|
229
|
+
const screenshotUploadOptions = [];
|
|
191
230
|
for (const locale of localesToPush) {
|
|
192
231
|
try {
|
|
193
232
|
const localeData = googlePlayData.locales[locale];
|
|
@@ -245,25 +284,40 @@ export class GooglePlayService {
|
|
|
245
284
|
skippedLocales.push(locale);
|
|
246
285
|
continue;
|
|
247
286
|
}
|
|
248
|
-
console.error(`[GooglePlay]
|
|
287
|
+
console.error(`[GooglePlay] 📋 Queued screenshots for ${locale} (batch mode - will replace existing)...`);
|
|
249
288
|
// Google Play upload strategy:
|
|
250
289
|
// - phone → uploads to phoneScreenshots AND sevenInchScreenshots (both use same images)
|
|
251
290
|
// - tablet → uploads to tenInchScreenshots only
|
|
252
|
-
|
|
291
|
+
screenshotUploadOptions.push({
|
|
253
292
|
language: locale,
|
|
254
293
|
phoneScreenshots: screenshots.phone,
|
|
255
294
|
sevenInchScreenshots: screenshots.phone,
|
|
256
295
|
tenInchScreenshots: screenshots.tablet,
|
|
257
296
|
featureGraphic: screenshots.featureGraphic || undefined,
|
|
297
|
+
imageUploadTimeoutMs,
|
|
258
298
|
});
|
|
259
|
-
console.error(`[GooglePlay] ✅ Images uploaded for ${locale}: ${uploadResult.uploaded.phoneScreenshots} phone, ${uploadResult.uploaded.sevenInchScreenshots} 7-inch, ${uploadResult.uploaded.tenInchScreenshots} 10-inch, feature graphic ${uploadResult.uploaded.featureGraphic ? "yes" : "no"}`);
|
|
260
|
-
uploadedLocales.push(locale);
|
|
261
299
|
}
|
|
262
300
|
catch (error) {
|
|
263
301
|
console.error(`[GooglePlay] ❌ Failed to upload screenshots for ${locale}: ${error instanceof Error ? error.message : String(error)}`);
|
|
264
302
|
failedLocales.push(locale);
|
|
265
303
|
}
|
|
266
304
|
}
|
|
305
|
+
for (let offset = 0; offset < screenshotUploadOptions.length; offset += GOOGLE_PLAY_SCREENSHOT_LOCALE_BATCH_SIZE) {
|
|
306
|
+
const batch = screenshotUploadOptions.slice(offset, offset + GOOGLE_PLAY_SCREENSHOT_LOCALE_BATCH_SIZE);
|
|
307
|
+
try {
|
|
308
|
+
console.error(`[GooglePlay] 📤 Uploading screenshots for ${batch.length} locale(s) in one edit...`);
|
|
309
|
+
const uploadResults = await client.uploadScreenshotsForLocales(batch);
|
|
310
|
+
for (const uploadResult of uploadResults) {
|
|
311
|
+
console.error(`[GooglePlay] ✅ Images uploaded for ${uploadResult.language}: ${uploadResult.uploaded.phoneScreenshots} phone, ${uploadResult.uploaded.sevenInchScreenshots} 7-inch, ${uploadResult.uploaded.tenInchScreenshots} 10-inch, feature graphic ${uploadResult.uploaded.featureGraphic ? "yes" : "no"}`);
|
|
312
|
+
uploadedLocales.push(uploadResult.language);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
const failedBatchLocales = batch.map((option) => option.language);
|
|
317
|
+
console.error(`[GooglePlay] ❌ Batch screenshot upload failed for ${failedBatchLocales.join(", ")}: ${error instanceof Error ? error.message : String(error)}`);
|
|
318
|
+
failedLocales.push(...failedBatchLocales);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
267
321
|
console.error(`[GooglePlay] 📊 Screenshot upload summary: ${uploadedLocales.length} succeeded, ${skippedLocales.length} skipped, ${failedLocales.length} failed`);
|
|
268
322
|
if (uploadedLocales.length > 0) {
|
|
269
323
|
console.error(`[GooglePlay] ✅ Uploaded: ${uploadedLocales.join(", ")}`);
|
|
@@ -272,7 +326,9 @@ export class GooglePlayService {
|
|
|
272
326
|
console.error(`[GooglePlay] ⏭️ Skipped: ${skippedLocales.join(", ")}`);
|
|
273
327
|
}
|
|
274
328
|
if (failedLocales.length > 0) {
|
|
275
|
-
|
|
329
|
+
const uniqueFailedLocales = [...new Set(failedLocales)];
|
|
330
|
+
console.error(`[GooglePlay] ❌ Failed: ${uniqueFailedLocales.join(", ")}`);
|
|
331
|
+
throw new Error(`Screenshot upload failed for locales: ${uniqueFailedLocales.join(", ")}`);
|
|
276
332
|
}
|
|
277
333
|
}
|
|
278
334
|
try {
|
package/dist/src/index.js
CHANGED
|
@@ -195,6 +195,16 @@ registerToolWithInfo("aso-push", {
|
|
|
195
195
|
.boolean()
|
|
196
196
|
.optional()
|
|
197
197
|
.describe("Whether to upload images as well"),
|
|
198
|
+
locales: z
|
|
199
|
+
.array(z.string())
|
|
200
|
+
.optional()
|
|
201
|
+
.describe("Optional locale allowlist to push (e.g. ['en-US', 'ko-KR'])"),
|
|
202
|
+
imageUploadTimeoutMs: z
|
|
203
|
+
.number()
|
|
204
|
+
.int()
|
|
205
|
+
.positive()
|
|
206
|
+
.optional()
|
|
207
|
+
.describe("Per-image upload timeout in milliseconds"),
|
|
198
208
|
dryRun: z
|
|
199
209
|
.boolean()
|
|
200
210
|
.optional()
|
|
@@ -42,6 +42,7 @@ export declare const ERROR_CODES: {
|
|
|
42
42
|
readonly APP_STORE_PULL_RELEASE_NOTES_FAILED: "APP_STORE_PULL_RELEASE_NOTES_FAILED";
|
|
43
43
|
readonly APP_STORE_CREATE_VERSION_FAILED: "APP_STORE_CREATE_VERSION_FAILED";
|
|
44
44
|
readonly APP_STORE_PUSH_FAILED: "APP_STORE_PUSH_FAILED";
|
|
45
|
+
readonly APP_STORE_ASO_DATA_EMPTY: "APP_STORE_ASO_DATA_EMPTY";
|
|
45
46
|
readonly APP_STORE_STATE_ERROR: "APP_STORE_STATE_ERROR";
|
|
46
47
|
readonly APP_STORE_CREATE_VERSION_FOR_STATE_ERROR_FAILED: "APP_STORE_CREATE_VERSION_FOR_STATE_ERROR_FAILED";
|
|
47
48
|
readonly APP_STORE_VERIFY_AUTH_FAILED: "APP_STORE_VERIFY_AUTH_FAILED";
|
|
@@ -53,6 +54,7 @@ export declare const ERROR_CODES: {
|
|
|
53
54
|
readonly GOOGLE_PLAY_PULL_RELEASE_NOTES_FAILED: "GOOGLE_PLAY_PULL_RELEASE_NOTES_FAILED";
|
|
54
55
|
readonly GOOGLE_PLAY_CREATE_VERSION_FAILED: "GOOGLE_PLAY_CREATE_VERSION_FAILED";
|
|
55
56
|
readonly GOOGLE_PLAY_PUSH_FAILED: "GOOGLE_PLAY_PUSH_FAILED";
|
|
57
|
+
readonly GOOGLE_PLAY_ASO_DATA_EMPTY: "GOOGLE_PLAY_ASO_DATA_EMPTY";
|
|
56
58
|
readonly GOOGLE_PLAY_VERIFY_AUTH_FAILED: "GOOGLE_PLAY_VERIFY_AUTH_FAILED";
|
|
57
59
|
readonly ASO_GOOGLE_PLAY_DATA_PARSE_FAILED: "ASO_GOOGLE_PLAY_DATA_PARSE_FAILED";
|
|
58
60
|
readonly ASO_APP_STORE_DATA_PARSE_FAILED: "ASO_APP_STORE_DATA_PARSE_FAILED";
|
|
@@ -49,6 +49,7 @@ export const ERROR_CODES = {
|
|
|
49
49
|
APP_STORE_PULL_RELEASE_NOTES_FAILED: "APP_STORE_PULL_RELEASE_NOTES_FAILED",
|
|
50
50
|
APP_STORE_CREATE_VERSION_FAILED: "APP_STORE_CREATE_VERSION_FAILED",
|
|
51
51
|
APP_STORE_PUSH_FAILED: "APP_STORE_PUSH_FAILED",
|
|
52
|
+
APP_STORE_ASO_DATA_EMPTY: "APP_STORE_ASO_DATA_EMPTY",
|
|
52
53
|
APP_STORE_STATE_ERROR: "APP_STORE_STATE_ERROR",
|
|
53
54
|
APP_STORE_CREATE_VERSION_FOR_STATE_ERROR_FAILED: "APP_STORE_CREATE_VERSION_FOR_STATE_ERROR_FAILED",
|
|
54
55
|
APP_STORE_VERIFY_AUTH_FAILED: "APP_STORE_VERIFY_AUTH_FAILED",
|
|
@@ -61,6 +62,7 @@ export const ERROR_CODES = {
|
|
|
61
62
|
GOOGLE_PLAY_PULL_RELEASE_NOTES_FAILED: "GOOGLE_PLAY_PULL_RELEASE_NOTES_FAILED",
|
|
62
63
|
GOOGLE_PLAY_CREATE_VERSION_FAILED: "GOOGLE_PLAY_CREATE_VERSION_FAILED",
|
|
63
64
|
GOOGLE_PLAY_PUSH_FAILED: "GOOGLE_PLAY_PUSH_FAILED",
|
|
65
|
+
GOOGLE_PLAY_ASO_DATA_EMPTY: "GOOGLE_PLAY_ASO_DATA_EMPTY",
|
|
64
66
|
GOOGLE_PLAY_VERIFY_AUTH_FAILED: "GOOGLE_PLAY_VERIFY_AUTH_FAILED",
|
|
65
67
|
// ASO data/files
|
|
66
68
|
ASO_GOOGLE_PLAY_DATA_PARSE_FAILED: "ASO_GOOGLE_PLAY_DATA_PARSE_FAILED",
|
|
@@ -82,6 +82,7 @@ export declare class AppStoreClient {
|
|
|
82
82
|
imagePath: string;
|
|
83
83
|
screenshotDisplayType: string;
|
|
84
84
|
locale: string;
|
|
85
|
+
imageUploadTimeoutMs?: number;
|
|
85
86
|
}): Promise<void>;
|
|
86
87
|
/**
|
|
87
88
|
* Find or create Screenshot Set for a specific display type
|
|
@@ -124,6 +125,7 @@ export declare class AppStoreClient {
|
|
|
124
125
|
displayType: string;
|
|
125
126
|
filename: string;
|
|
126
127
|
}>;
|
|
128
|
+
imageUploadTimeoutMs?: number;
|
|
127
129
|
}): Promise<{
|
|
128
130
|
uploaded: number;
|
|
129
131
|
deleted: number;
|
|
@@ -577,7 +577,7 @@ export class AppStoreClient {
|
|
|
577
577
|
* 4. Commit upload operation
|
|
578
578
|
*/
|
|
579
579
|
async uploadScreenshot(options) {
|
|
580
|
-
const { imagePath, screenshotDisplayType, locale } = options;
|
|
580
|
+
const { imagePath, screenshotDisplayType, locale, imageUploadTimeoutMs } = options;
|
|
581
581
|
try {
|
|
582
582
|
// Get app and version info
|
|
583
583
|
const appId = await this.findAppId();
|
|
@@ -611,7 +611,7 @@ export class AppStoreClient {
|
|
|
611
611
|
if (screenshot.uploadOperations &&
|
|
612
612
|
screenshot.uploadOperations.length > 0) {
|
|
613
613
|
const uploadOp = screenshot.uploadOperations[0];
|
|
614
|
-
await this.uploadFileToUrl(uploadOp.url, fileBuffer, uploadOp.method);
|
|
614
|
+
await this.uploadFileToUrl(uploadOp.url, fileBuffer, uploadOp.method, imageUploadTimeoutMs);
|
|
615
615
|
}
|
|
616
616
|
// Step 4: Commit screenshot
|
|
617
617
|
await this.commitAppScreenshot(screenshot.id);
|
|
@@ -703,7 +703,7 @@ export class AppStoreClient {
|
|
|
703
703
|
/**
|
|
704
704
|
* Upload file to reserved URL
|
|
705
705
|
*/
|
|
706
|
-
async uploadFileToUrl(url, fileBuffer, method = "PUT") {
|
|
706
|
+
async uploadFileToUrl(url, fileBuffer, method = "PUT", timeoutMs) {
|
|
707
707
|
const https = await import("node:https");
|
|
708
708
|
const { URL } = await import("node:url");
|
|
709
709
|
return new Promise((resolve, reject) => {
|
|
@@ -727,6 +727,11 @@ export class AppStoreClient {
|
|
|
727
727
|
}
|
|
728
728
|
});
|
|
729
729
|
req.on("error", reject);
|
|
730
|
+
if (timeoutMs) {
|
|
731
|
+
req.setTimeout(timeoutMs, () => {
|
|
732
|
+
req.destroy(new Error(`Upload timed out after ${timeoutMs}ms`));
|
|
733
|
+
});
|
|
734
|
+
}
|
|
730
735
|
req.write(fileBuffer);
|
|
731
736
|
req.end();
|
|
732
737
|
});
|
|
@@ -776,6 +781,10 @@ export class AppStoreClient {
|
|
|
776
781
|
const screenshots = screenshotsResponse.data || [];
|
|
777
782
|
let deletedCount = 0;
|
|
778
783
|
for (const screenshot of screenshots) {
|
|
784
|
+
if (screenshot.type !== "appScreenshots") {
|
|
785
|
+
console.error(`[AppStore] Skipping non-screenshot asset ${screenshot.id}`);
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
779
788
|
await this.deleteScreenshot(screenshot.id);
|
|
780
789
|
deletedCount++;
|
|
781
790
|
}
|
|
@@ -853,7 +862,7 @@ export class AppStoreClient {
|
|
|
853
862
|
* 3. Upload new screenshots in order
|
|
854
863
|
*/
|
|
855
864
|
async uploadScreenshotsForLocale(options) {
|
|
856
|
-
const { locale, screenshots } = options;
|
|
865
|
+
const { locale, screenshots, imageUploadTimeoutMs } = options;
|
|
857
866
|
const result = { uploaded: 0, deleted: 0, failed: 0 };
|
|
858
867
|
if (screenshots.length === 0) {
|
|
859
868
|
return result;
|
|
@@ -914,7 +923,7 @@ export class AppStoreClient {
|
|
|
914
923
|
if (screenshotData.uploadOperations &&
|
|
915
924
|
screenshotData.uploadOperations.length > 0) {
|
|
916
925
|
const uploadOp = screenshotData.uploadOperations[0];
|
|
917
|
-
await this.uploadFileToUrl(uploadOp.url, fileBuffer, uploadOp.method);
|
|
926
|
+
await this.uploadFileToUrl(uploadOp.url, fileBuffer, uploadOp.method, imageUploadTimeoutMs);
|
|
918
927
|
}
|
|
919
928
|
// Commit screenshot
|
|
920
929
|
await this.commitAppScreenshot(screenshotData.id);
|
|
@@ -55,6 +55,9 @@ export declare class GooglePlayClient {
|
|
|
55
55
|
* Deletes existing screenshots before uploading new ones
|
|
56
56
|
*/
|
|
57
57
|
uploadScreenshotsForLocale(options: BatchUploadScreenshotsOptions): Promise<BatchUploadScreenshotsResult>;
|
|
58
|
+
uploadScreenshotsForLocales(optionsList: BatchUploadScreenshotsOptions[]): Promise<BatchUploadScreenshotsResult[]>;
|
|
59
|
+
private uploadScreenshotsInSession;
|
|
60
|
+
private uploadImageWithOptionalTimeout;
|
|
58
61
|
private getTrack;
|
|
59
62
|
private updateTrack;
|
|
60
63
|
private listTracks;
|
|
@@ -620,7 +620,6 @@ export class GooglePlayClient {
|
|
|
620
620
|
* Deletes existing screenshots before uploading new ones
|
|
621
621
|
*/
|
|
622
622
|
async uploadScreenshotsForLocale(options) {
|
|
623
|
-
const { language, phoneScreenshots = [], sevenInchScreenshots = [], tenInchScreenshots = [], featureGraphic, } = options;
|
|
624
623
|
const authClient = await this.auth.getClient();
|
|
625
624
|
const editResponse = await this.createEdit(authClient, this.packageName);
|
|
626
625
|
const editId = editResponse.data.id;
|
|
@@ -629,6 +628,61 @@ export class GooglePlayClient {
|
|
|
629
628
|
packageName: this.packageName,
|
|
630
629
|
editId,
|
|
631
630
|
};
|
|
631
|
+
try {
|
|
632
|
+
const result = await this.uploadScreenshotsInSession(session, options);
|
|
633
|
+
// Commit all changes
|
|
634
|
+
console.error(`[GooglePlayClient] Committing screenshots for ${options.language}...`);
|
|
635
|
+
await this.commitEdit(session);
|
|
636
|
+
console.error(`[GooglePlayClient] ✅ Screenshots committed for ${options.language}`);
|
|
637
|
+
return result;
|
|
638
|
+
}
|
|
639
|
+
catch (error) {
|
|
640
|
+
console.error(`[GooglePlayClient] Rolling back screenshot upload for ${options.language}...`);
|
|
641
|
+
try {
|
|
642
|
+
await this.deleteEdit(session);
|
|
643
|
+
}
|
|
644
|
+
catch {
|
|
645
|
+
// Ignore deletion failure
|
|
646
|
+
}
|
|
647
|
+
throw error;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
async uploadScreenshotsForLocales(optionsList) {
|
|
651
|
+
if (optionsList.length === 0)
|
|
652
|
+
return [];
|
|
653
|
+
const authClient = await this.auth.getClient();
|
|
654
|
+
const editResponse = await this.createEdit(authClient, this.packageName);
|
|
655
|
+
const editId = editResponse.data.id;
|
|
656
|
+
const session = {
|
|
657
|
+
auth: authClient,
|
|
658
|
+
packageName: this.packageName,
|
|
659
|
+
editId,
|
|
660
|
+
};
|
|
661
|
+
try {
|
|
662
|
+
const results = [];
|
|
663
|
+
for (const options of optionsList) {
|
|
664
|
+
console.error(`[GooglePlayClient] Preparing screenshots for ${options.language}...`);
|
|
665
|
+
const result = await this.uploadScreenshotsInSession(session, options);
|
|
666
|
+
results.push(result);
|
|
667
|
+
}
|
|
668
|
+
console.error(`[GooglePlayClient] Committing screenshots for ${optionsList.length} locale(s)...`);
|
|
669
|
+
await this.commitEdit(session);
|
|
670
|
+
console.error(`[GooglePlayClient] ✅ Screenshots committed for ${optionsList.length} locale(s)`);
|
|
671
|
+
return results;
|
|
672
|
+
}
|
|
673
|
+
catch (error) {
|
|
674
|
+
console.error(`[GooglePlayClient] Rolling back batch screenshot upload...`);
|
|
675
|
+
try {
|
|
676
|
+
await this.deleteEdit(session);
|
|
677
|
+
}
|
|
678
|
+
catch {
|
|
679
|
+
// Ignore deletion failure
|
|
680
|
+
}
|
|
681
|
+
throw error;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
async uploadScreenshotsInSession(session, options) {
|
|
685
|
+
const { language, phoneScreenshots = [], sevenInchScreenshots = [], tenInchScreenshots = [], featureGraphic, imageUploadTimeoutMs, } = options;
|
|
632
686
|
const result = {
|
|
633
687
|
language,
|
|
634
688
|
uploaded: {
|
|
@@ -638,155 +692,107 @@ export class GooglePlayClient {
|
|
|
638
692
|
featureGraphic: false,
|
|
639
693
|
},
|
|
640
694
|
};
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
try {
|
|
646
|
-
await this.deleteAllImages(session, language, "phoneScreenshots");
|
|
647
|
-
}
|
|
648
|
-
catch (e) {
|
|
649
|
-
// Ignore if no images exist
|
|
650
|
-
if (e.code !== 404) {
|
|
651
|
-
console.error(`[GooglePlayClient] Warning: Failed to delete phone screenshots: ${e.message}`);
|
|
652
|
-
}
|
|
653
|
-
}
|
|
695
|
+
if (phoneScreenshots.length > 0) {
|
|
696
|
+
console.error(`[GooglePlayClient] Deleting existing phone screenshots for ${language}...`);
|
|
697
|
+
try {
|
|
698
|
+
await this.deleteAllImages(session, language, "phoneScreenshots");
|
|
654
699
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
}
|
|
660
|
-
catch (e) {
|
|
661
|
-
if (e.code !== 404) {
|
|
662
|
-
console.error(`[GooglePlayClient] Warning: Failed to delete 7-inch screenshots: ${e.message}`);
|
|
663
|
-
}
|
|
700
|
+
catch (e) {
|
|
701
|
+
// Ignore if no images exist
|
|
702
|
+
if (e.code !== 404) {
|
|
703
|
+
console.error(`[GooglePlayClient] Warning: Failed to delete phone screenshots: ${e.message}`);
|
|
664
704
|
}
|
|
665
705
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
catch (e) {
|
|
672
|
-
if (e.code !== 404) {
|
|
673
|
-
console.error(`[GooglePlayClient] Warning: Failed to delete 10-inch screenshots: ${e.message}`);
|
|
674
|
-
}
|
|
675
|
-
}
|
|
706
|
+
}
|
|
707
|
+
if (sevenInchScreenshots.length > 0) {
|
|
708
|
+
console.error(`[GooglePlayClient] Deleting existing 7-inch screenshots for ${language}...`);
|
|
709
|
+
try {
|
|
710
|
+
await this.deleteAllImages(session, language, "sevenInchScreenshots");
|
|
676
711
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
await this.deleteAllImages(session, language, "featureGraphic");
|
|
681
|
-
}
|
|
682
|
-
catch (e) {
|
|
683
|
-
if (e.code !== 404) {
|
|
684
|
-
console.error(`[GooglePlayClient] Warning: Failed to delete feature graphic: ${e.message}`);
|
|
685
|
-
}
|
|
712
|
+
catch (e) {
|
|
713
|
+
if (e.code !== 404) {
|
|
714
|
+
console.error(`[GooglePlayClient] Warning: Failed to delete 7-inch screenshots: ${e.message}`);
|
|
686
715
|
}
|
|
687
716
|
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
await this.androidPublisher.edits.images.upload({
|
|
698
|
-
auth: session.auth,
|
|
699
|
-
packageName: session.packageName,
|
|
700
|
-
editId: session.editId,
|
|
701
|
-
language,
|
|
702
|
-
imageType: "phoneScreenshots",
|
|
703
|
-
media: {
|
|
704
|
-
mimeType: "image/png",
|
|
705
|
-
body: imageBuffer,
|
|
706
|
-
},
|
|
707
|
-
});
|
|
708
|
-
console.error(`[GooglePlayClient] ✅ Uploaded ${fileName}`);
|
|
709
|
-
result.uploaded.phoneScreenshots++;
|
|
710
|
-
}
|
|
711
|
-
// Upload 7-inch tablet screenshots
|
|
712
|
-
for (let i = 0; i < sevenInchScreenshots.length; i++) {
|
|
713
|
-
const imagePath = sevenInchScreenshots[i];
|
|
714
|
-
if (!existsSync(imagePath)) {
|
|
715
|
-
console.error(`[GooglePlayClient] Warning: 7-inch screenshot not found: ${imagePath}`);
|
|
716
|
-
continue;
|
|
717
|
-
}
|
|
718
|
-
const imageBuffer = readFileSync(imagePath);
|
|
719
|
-
const fileName = imagePath.split("/").pop() || `tablet7-${i + 1}.png`;
|
|
720
|
-
await this.androidPublisher.edits.images.upload({
|
|
721
|
-
auth: session.auth,
|
|
722
|
-
packageName: session.packageName,
|
|
723
|
-
editId: session.editId,
|
|
724
|
-
language,
|
|
725
|
-
imageType: "sevenInchScreenshots",
|
|
726
|
-
media: {
|
|
727
|
-
mimeType: "image/png",
|
|
728
|
-
body: imageBuffer,
|
|
729
|
-
},
|
|
730
|
-
});
|
|
731
|
-
console.error(`[GooglePlayClient] ✅ Uploaded ${fileName}`);
|
|
732
|
-
result.uploaded.sevenInchScreenshots++;
|
|
733
|
-
}
|
|
734
|
-
// Upload 10-inch tablet screenshots
|
|
735
|
-
for (let i = 0; i < tenInchScreenshots.length; i++) {
|
|
736
|
-
const imagePath = tenInchScreenshots[i];
|
|
737
|
-
if (!existsSync(imagePath)) {
|
|
738
|
-
console.error(`[GooglePlayClient] Warning: 10-inch screenshot not found: ${imagePath}`);
|
|
739
|
-
continue;
|
|
717
|
+
}
|
|
718
|
+
if (tenInchScreenshots.length > 0) {
|
|
719
|
+
console.error(`[GooglePlayClient] Deleting existing 10-inch screenshots for ${language}...`);
|
|
720
|
+
try {
|
|
721
|
+
await this.deleteAllImages(session, language, "tenInchScreenshots");
|
|
722
|
+
}
|
|
723
|
+
catch (e) {
|
|
724
|
+
if (e.code !== 404) {
|
|
725
|
+
console.error(`[GooglePlayClient] Warning: Failed to delete 10-inch screenshots: ${e.message}`);
|
|
740
726
|
}
|
|
741
|
-
const imageBuffer = readFileSync(imagePath);
|
|
742
|
-
const fileName = imagePath.split("/").pop() || `tablet10-${i + 1}.png`;
|
|
743
|
-
await this.androidPublisher.edits.images.upload({
|
|
744
|
-
auth: session.auth,
|
|
745
|
-
packageName: session.packageName,
|
|
746
|
-
editId: session.editId,
|
|
747
|
-
language,
|
|
748
|
-
imageType: "tenInchScreenshots",
|
|
749
|
-
media: {
|
|
750
|
-
mimeType: "image/png",
|
|
751
|
-
body: imageBuffer,
|
|
752
|
-
},
|
|
753
|
-
});
|
|
754
|
-
console.error(`[GooglePlayClient] ✅ Uploaded ${fileName}`);
|
|
755
|
-
result.uploaded.tenInchScreenshots++;
|
|
756
|
-
}
|
|
757
|
-
// Upload feature graphic
|
|
758
|
-
if (featureGraphic && existsSync(featureGraphic)) {
|
|
759
|
-
const imageBuffer = readFileSync(featureGraphic);
|
|
760
|
-
await this.androidPublisher.edits.images.upload({
|
|
761
|
-
auth: session.auth,
|
|
762
|
-
packageName: session.packageName,
|
|
763
|
-
editId: session.editId,
|
|
764
|
-
language,
|
|
765
|
-
imageType: "featureGraphic",
|
|
766
|
-
media: {
|
|
767
|
-
mimeType: "image/png",
|
|
768
|
-
body: imageBuffer,
|
|
769
|
-
},
|
|
770
|
-
});
|
|
771
|
-
console.error(`[GooglePlayClient] ✅ Uploaded feature-graphic.png`);
|
|
772
|
-
result.uploaded.featureGraphic = true;
|
|
773
727
|
}
|
|
774
|
-
// Commit all changes
|
|
775
|
-
console.error(`[GooglePlayClient] Committing screenshots for ${language}...`);
|
|
776
|
-
await this.commitEdit(session);
|
|
777
|
-
console.error(`[GooglePlayClient] ✅ Screenshots committed for ${language}`);
|
|
778
|
-
return result;
|
|
779
728
|
}
|
|
780
|
-
|
|
781
|
-
console.error(`[GooglePlayClient]
|
|
729
|
+
if (featureGraphic) {
|
|
730
|
+
console.error(`[GooglePlayClient] Deleting existing feature graphic for ${language}...`);
|
|
782
731
|
try {
|
|
783
|
-
await this.
|
|
732
|
+
await this.deleteAllImages(session, language, "featureGraphic");
|
|
784
733
|
}
|
|
785
|
-
catch {
|
|
786
|
-
|
|
734
|
+
catch (e) {
|
|
735
|
+
if (e.code !== 404) {
|
|
736
|
+
console.error(`[GooglePlayClient] Warning: Failed to delete feature graphic: ${e.message}`);
|
|
737
|
+
}
|
|
787
738
|
}
|
|
788
|
-
throw error;
|
|
789
739
|
}
|
|
740
|
+
for (let i = 0; i < phoneScreenshots.length; i++) {
|
|
741
|
+
const imagePath = phoneScreenshots[i];
|
|
742
|
+
if (!existsSync(imagePath)) {
|
|
743
|
+
console.error(`[GooglePlayClient] Warning: Phone screenshot not found: ${imagePath}`);
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
const imageBuffer = readFileSync(imagePath);
|
|
747
|
+
const fileName = imagePath.split("/").pop() || `phone-${i + 1}.png`;
|
|
748
|
+
await this.uploadImageWithOptionalTimeout(session, language, "phoneScreenshots", imageBuffer, imageUploadTimeoutMs);
|
|
749
|
+
console.error(`[GooglePlayClient] ✅ Uploaded ${fileName}`);
|
|
750
|
+
result.uploaded.phoneScreenshots++;
|
|
751
|
+
}
|
|
752
|
+
for (let i = 0; i < sevenInchScreenshots.length; i++) {
|
|
753
|
+
const imagePath = sevenInchScreenshots[i];
|
|
754
|
+
if (!existsSync(imagePath)) {
|
|
755
|
+
console.error(`[GooglePlayClient] Warning: 7-inch screenshot not found: ${imagePath}`);
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
const imageBuffer = readFileSync(imagePath);
|
|
759
|
+
const fileName = imagePath.split("/").pop() || `tablet7-${i + 1}.png`;
|
|
760
|
+
await this.uploadImageWithOptionalTimeout(session, language, "sevenInchScreenshots", imageBuffer, imageUploadTimeoutMs);
|
|
761
|
+
console.error(`[GooglePlayClient] ✅ Uploaded ${fileName}`);
|
|
762
|
+
result.uploaded.sevenInchScreenshots++;
|
|
763
|
+
}
|
|
764
|
+
for (let i = 0; i < tenInchScreenshots.length; i++) {
|
|
765
|
+
const imagePath = tenInchScreenshots[i];
|
|
766
|
+
if (!existsSync(imagePath)) {
|
|
767
|
+
console.error(`[GooglePlayClient] Warning: 10-inch screenshot not found: ${imagePath}`);
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
const imageBuffer = readFileSync(imagePath);
|
|
771
|
+
const fileName = imagePath.split("/").pop() || `tablet10-${i + 1}.png`;
|
|
772
|
+
await this.uploadImageWithOptionalTimeout(session, language, "tenInchScreenshots", imageBuffer, imageUploadTimeoutMs);
|
|
773
|
+
console.error(`[GooglePlayClient] ✅ Uploaded ${fileName}`);
|
|
774
|
+
result.uploaded.tenInchScreenshots++;
|
|
775
|
+
}
|
|
776
|
+
if (featureGraphic && existsSync(featureGraphic)) {
|
|
777
|
+
const imageBuffer = readFileSync(featureGraphic);
|
|
778
|
+
await this.uploadImageWithOptionalTimeout(session, language, "featureGraphic", imageBuffer, imageUploadTimeoutMs);
|
|
779
|
+
console.error(`[GooglePlayClient] ✅ Uploaded feature-graphic.png`);
|
|
780
|
+
result.uploaded.featureGraphic = true;
|
|
781
|
+
}
|
|
782
|
+
return result;
|
|
783
|
+
}
|
|
784
|
+
async uploadImageWithOptionalTimeout(session, language, imageType, imageBuffer, timeoutMs) {
|
|
785
|
+
await this.androidPublisher.edits.images.upload({
|
|
786
|
+
auth: session.auth,
|
|
787
|
+
packageName: session.packageName,
|
|
788
|
+
editId: session.editId,
|
|
789
|
+
language,
|
|
790
|
+
imageType,
|
|
791
|
+
media: {
|
|
792
|
+
mimeType: "image/png",
|
|
793
|
+
body: imageBuffer,
|
|
794
|
+
},
|
|
795
|
+
}, timeoutMs ? { timeout: timeoutMs } : undefined);
|
|
790
796
|
}
|
|
791
797
|
async getTrack(session, track) {
|
|
792
798
|
const response = await this.androidPublisher.edits.tracks.get({
|
|
@@ -9,7 +9,7 @@ const appResolutionService = new AppResolutionService();
|
|
|
9
9
|
const appStoreService = new AppStoreService();
|
|
10
10
|
const googlePlayService = new GooglePlayService();
|
|
11
11
|
export async function handleAsoPush(options) {
|
|
12
|
-
const { store = "both", uploadImages = false, dryRun = false } = options;
|
|
12
|
+
const { store = "both", uploadImages = false, locales, imageUploadTimeoutMs, dryRun = false, } = options;
|
|
13
13
|
const resolved = appResolutionService.resolve({
|
|
14
14
|
slug: options.app,
|
|
15
15
|
packageName: options.packageName,
|
|
@@ -34,6 +34,11 @@ export async function handleAsoPush(options) {
|
|
|
34
34
|
if (bundleId)
|
|
35
35
|
console.error(`[MCP] Bundle ID: ${bundleId}`);
|
|
36
36
|
console.error(`[MCP] Upload Images: ${uploadImages ? "Yes" : "No"}`);
|
|
37
|
+
if (locales?.length)
|
|
38
|
+
console.error(`[MCP] Locales: ${locales.join(", ")}`);
|
|
39
|
+
if (imageUploadTimeoutMs) {
|
|
40
|
+
console.error(`[MCP] Image Upload Timeout: ${imageUploadTimeoutMs}ms`);
|
|
41
|
+
}
|
|
37
42
|
console.error(`[MCP] Mode: ${dryRun ? "Dry run" : "Actual push"}`);
|
|
38
43
|
let config;
|
|
39
44
|
try {
|
|
@@ -136,6 +141,8 @@ export async function handleAsoPush(options) {
|
|
|
136
141
|
localAsoData: configData,
|
|
137
142
|
googlePlayDataPath,
|
|
138
143
|
uploadImages,
|
|
144
|
+
locales,
|
|
145
|
+
imageUploadTimeoutMs,
|
|
139
146
|
slug,
|
|
140
147
|
});
|
|
141
148
|
results.push(formatPushResult("Google Play", result));
|
|
@@ -153,6 +160,8 @@ export async function handleAsoPush(options) {
|
|
|
153
160
|
localAsoData: configData,
|
|
154
161
|
appStoreDataPath,
|
|
155
162
|
uploadImages,
|
|
163
|
+
locales,
|
|
164
|
+
imageUploadTimeoutMs,
|
|
156
165
|
slug,
|
|
157
166
|
});
|
|
158
167
|
results.push(formatPushResult("App Store", appStoreResult));
|