pabal-store-api-mcp 1.3.0 → 1.3.1
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.js +61 -31
- package/dist/src/core/services/google-play-service.js +72 -41
- package/dist/src/packages/configs/aso-config/types.d.ts +2 -0
- package/dist/src/packages/configs/aso-config/utils.js +11 -0
- package/dist/src/packages/stores/play-store/api-converters.d.ts +1 -0
- package/dist/src/packages/stores/play-store/api-converters.js +2 -0
- package/dist/src/packages/stores/play-store/client.js +10 -2
- package/dist/src/packages/stores/play-store/types.d.ts +1 -0
- package/package.json +1 -1
|
@@ -201,41 +201,71 @@ export class AppStoreService {
|
|
|
201
201
|
const { parseAppStoreScreenshots, hasScreenshots } = await import("../../core/helpers/screenshot-helpers.js");
|
|
202
202
|
const pushDataDir = getAsoPushDir();
|
|
203
203
|
const screenshotsBaseDir = `${pushDataDir}/products/${slug}/store/app-store/screenshots`;
|
|
204
|
+
const uploadedLocales = [];
|
|
205
|
+
const skippedLocales = [];
|
|
206
|
+
const failedLocales = [];
|
|
204
207
|
for (const locale of localesToPush) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
208
|
+
try {
|
|
209
|
+
if (!hasScreenshots(screenshotsBaseDir, locale)) {
|
|
210
|
+
console.error(`[AppStore] ⏭️ Skipping ${locale} - no screenshots directory`);
|
|
211
|
+
skippedLocales.push(locale);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const result = parseAppStoreScreenshots(screenshotsBaseDir, locale);
|
|
215
|
+
// Check if there are any valid screenshots
|
|
216
|
+
const totalScreenshots = Object.values(result.valid).reduce((sum, screenshots) => sum + screenshots.length, 0);
|
|
217
|
+
if (totalScreenshots === 0) {
|
|
218
|
+
console.error(`[AppStore] ⚠️ Skipping ${locale} - no valid screenshots found`);
|
|
219
|
+
skippedLocales.push(locale);
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
console.error(`[AppStore] 📤 Uploading screenshots for ${locale}...`);
|
|
223
|
+
// Report parsing issues
|
|
224
|
+
if (result.invalid.length > 0) {
|
|
225
|
+
console.error(`[AppStore] ⚠️ Invalid filenames: ${result.invalid.join(", ")}`);
|
|
226
|
+
}
|
|
227
|
+
if (result.unknown.length > 0) {
|
|
228
|
+
console.error(`[AppStore] ⚠️ Unknown device types: ${result.unknown.join(", ")}`);
|
|
229
|
+
}
|
|
230
|
+
// Upload screenshots for each device type
|
|
231
|
+
for (const [displayType, screenshots] of Object.entries(result.valid)) {
|
|
232
|
+
if (screenshots.length === 0)
|
|
233
|
+
continue;
|
|
234
|
+
console.error(`[AppStore] 📱 Uploading ${screenshots.length} screenshots for ${displayType}...`);
|
|
235
|
+
for (const screenshot of screenshots) {
|
|
236
|
+
try {
|
|
237
|
+
await client.uploadScreenshot({
|
|
238
|
+
imagePath: screenshot.path,
|
|
239
|
+
screenshotDisplayType: displayType,
|
|
240
|
+
locale,
|
|
241
|
+
});
|
|
242
|
+
console.error(`[AppStore] ✅ ${screenshot.filename}`);
|
|
243
|
+
}
|
|
244
|
+
catch (uploadError) {
|
|
245
|
+
const msg = uploadError instanceof Error
|
|
246
|
+
? uploadError.message
|
|
247
|
+
: String(uploadError);
|
|
248
|
+
console.error(`[AppStore] ❌ ${screenshot.filename}: ${msg}`);
|
|
249
|
+
}
|
|
235
250
|
}
|
|
236
251
|
}
|
|
252
|
+
uploadedLocales.push(locale);
|
|
253
|
+
console.error(`[AppStore] ✅ Screenshots uploaded for ${locale}`);
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
console.error(`[AppStore] ❌ Failed to upload screenshots for ${locale}: ${error instanceof Error ? error.message : String(error)}`);
|
|
257
|
+
failedLocales.push(locale);
|
|
237
258
|
}
|
|
238
|
-
|
|
259
|
+
}
|
|
260
|
+
console.error(`[AppStore] 📊 Screenshot upload summary: ${uploadedLocales.length} succeeded, ${skippedLocales.length} skipped, ${failedLocales.length} failed`);
|
|
261
|
+
if (uploadedLocales.length > 0) {
|
|
262
|
+
console.error(`[AppStore] ✅ Uploaded: ${uploadedLocales.join(", ")}`);
|
|
263
|
+
}
|
|
264
|
+
if (skippedLocales.length > 0) {
|
|
265
|
+
console.error(`[AppStore] ⏭️ Skipped: ${skippedLocales.join(", ")}`);
|
|
266
|
+
}
|
|
267
|
+
if (failedLocales.length > 0) {
|
|
268
|
+
console.error(`[AppStore] ❌ Failed: ${failedLocales.join(", ")}`);
|
|
239
269
|
}
|
|
240
270
|
}
|
|
241
271
|
try {
|
|
@@ -177,6 +177,7 @@ export class GooglePlayService {
|
|
|
177
177
|
});
|
|
178
178
|
console.error(`[GooglePlay] ✅ App details uploaded successfully`);
|
|
179
179
|
}
|
|
180
|
+
// Note: YouTube URL is pushed as part of listing data for each locale
|
|
180
181
|
// Upload screenshots if enabled
|
|
181
182
|
if (uploadImages && slug) {
|
|
182
183
|
console.error(`[GooglePlay] 📤 Uploading screenshots...`);
|
|
@@ -184,50 +185,80 @@ export class GooglePlayService {
|
|
|
184
185
|
const { parseGooglePlayScreenshots, hasScreenshots } = await import("../../core/helpers/screenshot-helpers.js");
|
|
185
186
|
const pushDataDir = getAsoPushDir();
|
|
186
187
|
const screenshotsBaseDir = `${pushDataDir}/products/${slug}/store/google-play/screenshots`;
|
|
188
|
+
const uploadedLocales = [];
|
|
189
|
+
const skippedLocales = [];
|
|
190
|
+
const failedLocales = [];
|
|
187
191
|
for (const locale of localesToPush) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
192
|
+
try {
|
|
193
|
+
if (!hasScreenshots(screenshotsBaseDir, locale)) {
|
|
194
|
+
console.error(`[GooglePlay] ⏭️ Skipping ${locale} - no screenshots directory`);
|
|
195
|
+
skippedLocales.push(locale);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const screenshots = parseGooglePlayScreenshots(screenshotsBaseDir, locale);
|
|
199
|
+
// Google Play requires minimum 2 phone screenshots
|
|
200
|
+
const phoneCount = screenshots.phone.length + screenshots.tablet7.length;
|
|
201
|
+
if (phoneCount < 2) {
|
|
202
|
+
console.error(`[GooglePlay] ⚠️ Skipping ${locale} - needs at least 2 phone/tablet7 screenshots (found ${phoneCount})`);
|
|
203
|
+
skippedLocales.push(locale);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
console.error(`[GooglePlay] 📤 Uploading screenshots for ${locale}...`);
|
|
207
|
+
// Upload phone screenshots (phone-*.png)
|
|
208
|
+
for (const imagePath of screenshots.phone) {
|
|
209
|
+
await client.uploadScreenshot({
|
|
210
|
+
imagePath,
|
|
211
|
+
imageType: "phoneScreenshots",
|
|
212
|
+
language: locale,
|
|
213
|
+
});
|
|
214
|
+
console.error(`[GooglePlay] ✅ ${imagePath.split("/").pop()}`);
|
|
215
|
+
}
|
|
216
|
+
// Upload 7-inch tablet screenshots as phone
|
|
217
|
+
for (const imagePath of screenshots.tablet7) {
|
|
218
|
+
await client.uploadScreenshot({
|
|
219
|
+
imagePath,
|
|
220
|
+
imageType: "phoneScreenshots",
|
|
221
|
+
language: locale,
|
|
222
|
+
});
|
|
223
|
+
console.error(`[GooglePlay] ✅ ${imagePath.split("/").pop()} (as phone)`);
|
|
224
|
+
}
|
|
225
|
+
// Upload 10-inch tablet screenshots as tablet (optional)
|
|
226
|
+
if (screenshots.tablet10.length > 0) {
|
|
227
|
+
for (const imagePath of screenshots.tablet10) {
|
|
228
|
+
await client.uploadScreenshot({
|
|
229
|
+
imagePath,
|
|
230
|
+
imageType: "tenInchScreenshots",
|
|
231
|
+
language: locale,
|
|
232
|
+
});
|
|
233
|
+
console.error(`[GooglePlay] ✅ ${imagePath.split("/").pop()} (as tablet)`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Upload feature graphic (optional)
|
|
237
|
+
if (screenshots.featureGraphic) {
|
|
238
|
+
await client.uploadScreenshot({
|
|
239
|
+
imagePath: screenshots.featureGraphic,
|
|
240
|
+
imageType: "featureGraphic",
|
|
241
|
+
language: locale,
|
|
242
|
+
});
|
|
243
|
+
console.error(`[GooglePlay] ✅ feature-graphic.png`);
|
|
244
|
+
}
|
|
245
|
+
uploadedLocales.push(locale);
|
|
246
|
+
console.error(`[GooglePlay] ✅ Screenshots uploaded for ${locale}`);
|
|
191
247
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
for (const imagePath of screenshots.phone) {
|
|
196
|
-
await client.uploadScreenshot({
|
|
197
|
-
imagePath,
|
|
198
|
-
imageType: "phoneScreenshots",
|
|
199
|
-
language: locale,
|
|
200
|
-
});
|
|
201
|
-
console.error(`[GooglePlay] ✅ ${imagePath.split("/").pop()}`);
|
|
248
|
+
catch (error) {
|
|
249
|
+
console.error(`[GooglePlay] ❌ Failed to upload screenshots for ${locale}: ${error instanceof Error ? error.message : String(error)}`);
|
|
250
|
+
failedLocales.push(locale);
|
|
202
251
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
for (const imagePath of screenshots.tablet10) {
|
|
214
|
-
await client.uploadScreenshot({
|
|
215
|
-
imagePath,
|
|
216
|
-
imageType: "tenInchScreenshots",
|
|
217
|
-
language: locale,
|
|
218
|
-
});
|
|
219
|
-
console.error(`[GooglePlay] ✅ ${imagePath.split("/").pop()} (as tablet)`);
|
|
220
|
-
}
|
|
221
|
-
// Upload feature graphic
|
|
222
|
-
if (screenshots.featureGraphic) {
|
|
223
|
-
await client.uploadScreenshot({
|
|
224
|
-
imagePath: screenshots.featureGraphic,
|
|
225
|
-
imageType: "featureGraphic",
|
|
226
|
-
language: locale,
|
|
227
|
-
});
|
|
228
|
-
console.error(`[GooglePlay] ✅ feature-graphic.png`);
|
|
229
|
-
}
|
|
230
|
-
console.error(`[GooglePlay] ✅ Screenshots uploaded for ${locale}`);
|
|
252
|
+
}
|
|
253
|
+
console.error(`[GooglePlay] 📊 Screenshot upload summary: ${uploadedLocales.length} succeeded, ${skippedLocales.length} skipped, ${failedLocales.length} failed`);
|
|
254
|
+
if (uploadedLocales.length > 0) {
|
|
255
|
+
console.error(`[GooglePlay] ✅ Uploaded: ${uploadedLocales.join(", ")}`);
|
|
256
|
+
}
|
|
257
|
+
if (skippedLocales.length > 0) {
|
|
258
|
+
console.error(`[GooglePlay] ⏭️ Skipped: ${skippedLocales.join(", ")}`);
|
|
259
|
+
}
|
|
260
|
+
if (failedLocales.length > 0) {
|
|
261
|
+
console.error(`[GooglePlay] ❌ Failed: ${failedLocales.join(", ")}`);
|
|
231
262
|
}
|
|
232
263
|
}
|
|
233
264
|
try {
|
|
@@ -17,6 +17,7 @@ export interface GooglePlayAsoData {
|
|
|
17
17
|
screenshots: GooglePlayScreenshots;
|
|
18
18
|
featureGraphic?: string;
|
|
19
19
|
promoGraphic?: string;
|
|
20
|
+
video?: string;
|
|
20
21
|
category?: string;
|
|
21
22
|
contentRating?: string;
|
|
22
23
|
keywords?: string[];
|
|
@@ -31,6 +32,7 @@ export interface GooglePlayMultilingualAsoData {
|
|
|
31
32
|
defaultLocale?: string;
|
|
32
33
|
contactEmail?: string;
|
|
33
34
|
contactWebsite?: string;
|
|
35
|
+
youtubeUrl?: string;
|
|
34
36
|
}
|
|
35
37
|
export interface GooglePlayReleaseNote {
|
|
36
38
|
versionCode: number;
|
|
@@ -72,12 +72,18 @@ export function prepareAsoDataForPush(slug, configData, options) {
|
|
|
72
72
|
const locales = isGooglePlayMultilingual(googlePlayData)
|
|
73
73
|
? googlePlayData.locales
|
|
74
74
|
: { [googlePlayData.defaultLanguage || DEFAULT_LOCALE]: googlePlayData };
|
|
75
|
+
// Get app-level youtubeUrl if available
|
|
76
|
+
const appLevelYoutubeUrl = isGooglePlayMultilingual(googlePlayData)
|
|
77
|
+
? googlePlayData.youtubeUrl
|
|
78
|
+
: undefined;
|
|
75
79
|
const cleanedLocales = {};
|
|
76
80
|
for (const [locale, localeData] of Object.entries(locales)) {
|
|
77
81
|
const { screenshots, featureGraphic, ...rest } = localeData;
|
|
78
82
|
cleanedLocales[locale] = {
|
|
79
83
|
...rest,
|
|
80
84
|
contactWebsite: detailPageUrl,
|
|
85
|
+
// Apply app-level youtubeUrl to all locales if not already set
|
|
86
|
+
video: rest.video || appLevelYoutubeUrl,
|
|
81
87
|
};
|
|
82
88
|
}
|
|
83
89
|
const gpDefaultLocale = isGooglePlayMultilingual(googlePlayData)
|
|
@@ -86,6 +92,11 @@ export function prepareAsoDataForPush(slug, configData, options) {
|
|
|
86
92
|
storeData.googlePlay = {
|
|
87
93
|
locales: cleanedLocales,
|
|
88
94
|
defaultLocale: gpDefaultLocale || DEFAULT_LOCALE,
|
|
95
|
+
contactEmail: isGooglePlayMultilingual(googlePlayData)
|
|
96
|
+
? googlePlayData.contactEmail
|
|
97
|
+
: undefined,
|
|
98
|
+
contactWebsite: detailPageUrl,
|
|
99
|
+
youtubeUrl: appLevelYoutubeUrl,
|
|
89
100
|
};
|
|
90
101
|
}
|
|
91
102
|
if (configData.appStore) {
|
|
@@ -121,6 +121,8 @@ export function buildListingRequestBody(data) {
|
|
|
121
121
|
body.shortDescription = data.shortDescription;
|
|
122
122
|
if (data.fullDescription)
|
|
123
123
|
body.fullDescription = data.fullDescription;
|
|
124
|
+
if (data.video)
|
|
125
|
+
body.video = data.video;
|
|
124
126
|
return body;
|
|
125
127
|
}
|
|
126
128
|
/**
|
|
@@ -138,11 +138,15 @@ export class GooglePlayClient {
|
|
|
138
138
|
};
|
|
139
139
|
try {
|
|
140
140
|
const language = data.defaultLanguage || DEFAULT_LANGUAGE;
|
|
141
|
-
if (data.title ||
|
|
141
|
+
if (data.title ||
|
|
142
|
+
data.shortDescription ||
|
|
143
|
+
data.fullDescription ||
|
|
144
|
+
data.video) {
|
|
142
145
|
const listingBody = buildListingRequestBody({
|
|
143
146
|
title: data.title,
|
|
144
147
|
shortDescription: data.shortDescription,
|
|
145
148
|
fullDescription: data.fullDescription,
|
|
149
|
+
video: data.video,
|
|
146
150
|
});
|
|
147
151
|
console.error(`[GooglePlayClient] Updating listing for ${language}:`, JSON.stringify(listingBody, null, 2));
|
|
148
152
|
try {
|
|
@@ -216,11 +220,13 @@ export class GooglePlayClient {
|
|
|
216
220
|
for (const [language, localeData] of Object.entries(data.locales)) {
|
|
217
221
|
if (localeData.title ||
|
|
218
222
|
localeData.shortDescription ||
|
|
219
|
-
localeData.fullDescription
|
|
223
|
+
localeData.fullDescription ||
|
|
224
|
+
localeData.video) {
|
|
220
225
|
const listingBody = buildListingRequestBody({
|
|
221
226
|
title: localeData.title,
|
|
222
227
|
shortDescription: localeData.shortDescription,
|
|
223
228
|
fullDescription: localeData.fullDescription,
|
|
229
|
+
video: localeData.video,
|
|
224
230
|
});
|
|
225
231
|
console.error(`[GooglePlayClient] Updating listing for ${language}...`);
|
|
226
232
|
try {
|
|
@@ -295,6 +301,8 @@ export class GooglePlayClient {
|
|
|
295
301
|
console.error(`[GooglePlayClient] No app details to update, skipping`);
|
|
296
302
|
return;
|
|
297
303
|
}
|
|
304
|
+
// Note: youtubeUrl is not part of AppDetails API
|
|
305
|
+
// YouTube URLs are managed at listing level (see pushMultilingualAsoData)
|
|
298
306
|
const authClient = await this.auth.getClient();
|
|
299
307
|
const editResponse = await this.createEdit(authClient, this.packageName);
|
|
300
308
|
const editId = editResponse.data.id;
|
|
@@ -120,6 +120,7 @@ export interface UpdateReleaseNotesOptions {
|
|
|
120
120
|
* App Details Data
|
|
121
121
|
* Internal type for app details update operations
|
|
122
122
|
* Based on AppDetails but with optional fields for partial updates
|
|
123
|
+
* Note: YouTube URLs are managed at listing level, not app details level
|
|
123
124
|
*/
|
|
124
125
|
export interface AppDetailsData {
|
|
125
126
|
contactEmail?: NonNullable<AppDetails["contactEmail"]>;
|