pabal-store-api-mcp 1.3.2 → 1.3.3
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/helpers/screenshot-helpers.js +17 -9
- package/dist/src/core/services/app-store-service.js +7 -19
- package/dist/src/core/services/google-play-service.js +14 -40
- package/dist/src/packages/stores/app-store/client.d.ts +26 -0
- package/dist/src/packages/stores/app-store/client.js +124 -0
- package/dist/src/packages/stores/play-store/client.d.ts +7 -1
- package/dist/src/packages/stores/play-store/client.js +182 -0
- package/dist/src/packages/stores/play-store/types.d.ts +25 -0
- package/package.json +1 -1
|
@@ -102,19 +102,27 @@ export function parseAppStoreScreenshots(screenshotsBaseDir, locale) {
|
|
|
102
102
|
unknown: unknownDeviceTypes,
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Sort screenshot filenames by numeric order
|
|
107
|
+
* e.g., phone-1.png, phone-2.png, phone-10.png (not phone-1, phone-10, phone-2)
|
|
108
|
+
*/
|
|
109
|
+
function sortByNumericOrder(files, prefix) {
|
|
110
|
+
return files.sort((a, b) => {
|
|
111
|
+
const numA = parseInt(a.replace(prefix, "").replace(".png", ""), 10);
|
|
112
|
+
const numB = parseInt(b.replace(prefix, "").replace(".png", ""), 10);
|
|
113
|
+
return numA - numB;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
105
116
|
export function parseGooglePlayScreenshots(screenshotsBaseDir, locale) {
|
|
106
117
|
const localeDir = `${screenshotsBaseDir}/${locale}`;
|
|
107
118
|
const files = getLocaleScreenshotFiles(screenshotsBaseDir, locale);
|
|
119
|
+
const phoneFiles = files.filter((f) => f.startsWith("phone-") && f.endsWith(".png"));
|
|
120
|
+
const tablet7Files = files.filter((f) => f.startsWith("tablet7-") && f.endsWith(".png"));
|
|
121
|
+
const tablet10Files = files.filter((f) => f.startsWith("tablet10-") && f.endsWith(".png"));
|
|
108
122
|
return {
|
|
109
|
-
phone:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
tablet7: files
|
|
113
|
-
.filter((f) => f.startsWith("tablet7-") && f.endsWith(".png"))
|
|
114
|
-
.map((f) => `${localeDir}/${f}`),
|
|
115
|
-
tablet10: files
|
|
116
|
-
.filter((f) => f.startsWith("tablet10-") && f.endsWith(".png"))
|
|
117
|
-
.map((f) => `${localeDir}/${f}`),
|
|
123
|
+
phone: sortByNumericOrder(phoneFiles, "phone-").map((f) => `${localeDir}/${f}`),
|
|
124
|
+
tablet7: sortByNumericOrder(tablet7Files, "tablet7-").map((f) => `${localeDir}/${f}`),
|
|
125
|
+
tablet10: sortByNumericOrder(tablet10Files, "tablet10-").map((f) => `${localeDir}/${f}`),
|
|
118
126
|
featureGraphic: files.includes("feature-graphic.png")
|
|
119
127
|
? `${localeDir}/feature-graphic.png`
|
|
120
128
|
: null,
|
|
@@ -273,26 +273,14 @@ export class AppStoreService {
|
|
|
273
273
|
skippedLocales.push(locale);
|
|
274
274
|
continue;
|
|
275
275
|
}
|
|
276
|
-
console.error(`[AppStore] 📤 Uploading screenshots for ${locale}...`);
|
|
277
|
-
//
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
locale,
|
|
284
|
-
});
|
|
285
|
-
console.error(`[AppStore] ✅ ${screenshot.filename}`);
|
|
286
|
-
}
|
|
287
|
-
catch (uploadError) {
|
|
288
|
-
const msg = uploadError instanceof Error
|
|
289
|
-
? uploadError.message
|
|
290
|
-
: String(uploadError);
|
|
291
|
-
console.error(`[AppStore] ❌ ${screenshot.filename}: ${msg}`);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
276
|
+
console.error(`[AppStore] 📤 Uploading screenshots for ${locale} (batch mode - will replace existing)...`);
|
|
277
|
+
// Use batch upload method - deletes existing and uploads new
|
|
278
|
+
const uploadResult = await client.uploadScreenshotsForLocale({
|
|
279
|
+
locale,
|
|
280
|
+
screenshots: screenshotsToUpload,
|
|
281
|
+
});
|
|
282
|
+
console.error(`[AppStore] ✅ Screenshots for ${locale}: ${uploadResult.uploaded} uploaded, ${uploadResult.deleted} deleted, ${uploadResult.failed} failed`);
|
|
294
283
|
uploadedLocales.push(locale);
|
|
295
|
-
console.error(`[AppStore] ✅ Screenshots uploaded for ${locale}`);
|
|
296
284
|
}
|
|
297
285
|
catch (error) {
|
|
298
286
|
console.error(`[AppStore] ❌ Failed to upload screenshots for ${locale}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -231,47 +231,21 @@ export class GooglePlayService {
|
|
|
231
231
|
skippedLocales.push(locale);
|
|
232
232
|
continue;
|
|
233
233
|
}
|
|
234
|
-
console.error(`[GooglePlay] 📤 Uploading screenshots for ${locale}...`);
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
imageType: "phoneScreenshots",
|
|
249
|
-
language: locale,
|
|
250
|
-
});
|
|
251
|
-
console.error(`[GooglePlay] ✅ ${imagePath.split("/").pop()} (as phone)`);
|
|
252
|
-
}
|
|
253
|
-
// Upload 10-inch tablet screenshots as tablet (optional)
|
|
254
|
-
if (screenshots.tablet10.length > 0) {
|
|
255
|
-
for (const imagePath of screenshots.tablet10) {
|
|
256
|
-
await client.uploadScreenshot({
|
|
257
|
-
imagePath,
|
|
258
|
-
imageType: "tenInchScreenshots",
|
|
259
|
-
language: locale,
|
|
260
|
-
});
|
|
261
|
-
console.error(`[GooglePlay] ✅ ${imagePath.split("/").pop()} (as tablet)`);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
// Upload feature graphic (optional)
|
|
265
|
-
if (screenshots.featureGraphic) {
|
|
266
|
-
await client.uploadScreenshot({
|
|
267
|
-
imagePath: screenshots.featureGraphic,
|
|
268
|
-
imageType: "featureGraphic",
|
|
269
|
-
language: locale,
|
|
270
|
-
});
|
|
271
|
-
console.error(`[GooglePlay] ✅ feature-graphic.png`);
|
|
272
|
-
}
|
|
234
|
+
console.error(`[GooglePlay] 📤 Uploading screenshots for ${locale} (batch mode - will replace existing)...`);
|
|
235
|
+
// Use batch upload method - deletes existing and uploads new in single edit session
|
|
236
|
+
// Combine phone and tablet7 screenshots as phoneScreenshots (Google Play accepts both sizes)
|
|
237
|
+
const phoneScreenshotPaths = [
|
|
238
|
+
...screenshots.phone,
|
|
239
|
+
...screenshots.tablet7,
|
|
240
|
+
];
|
|
241
|
+
const uploadResult = await client.uploadScreenshotsForLocale({
|
|
242
|
+
language: locale,
|
|
243
|
+
phoneScreenshots: phoneScreenshotPaths,
|
|
244
|
+
tenInchScreenshots: screenshots.tablet10,
|
|
245
|
+
featureGraphic: screenshots.featureGraphic || undefined,
|
|
246
|
+
});
|
|
247
|
+
console.error(`[GooglePlay] ✅ Screenshots uploaded for ${locale}: ${uploadResult.uploaded.phoneScreenshots} phone, ${uploadResult.uploaded.tenInchScreenshots} tablet`);
|
|
273
248
|
uploadedLocales.push(locale);
|
|
274
|
-
console.error(`[GooglePlay] ✅ Screenshots uploaded for ${locale}`);
|
|
275
249
|
}
|
|
276
250
|
catch (error) {
|
|
277
251
|
console.error(`[GooglePlay] ❌ Failed to upload screenshots for ${locale}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -99,6 +99,32 @@ export declare class AppStoreClient {
|
|
|
99
99
|
* Commit AppScreenshot after upload
|
|
100
100
|
*/
|
|
101
101
|
private commitAppScreenshot;
|
|
102
|
+
/**
|
|
103
|
+
* Delete a single screenshot
|
|
104
|
+
*/
|
|
105
|
+
private deleteScreenshot;
|
|
106
|
+
/**
|
|
107
|
+
* Delete all screenshots in a screenshot set
|
|
108
|
+
*/
|
|
109
|
+
private deleteAllScreenshotsInSet;
|
|
110
|
+
/**
|
|
111
|
+
* Upload multiple screenshots for a locale, replacing existing ones
|
|
112
|
+
* 1. Find or create screenshot sets for each display type
|
|
113
|
+
* 2. Delete existing screenshots in each set
|
|
114
|
+
* 3. Upload new screenshots in order
|
|
115
|
+
*/
|
|
116
|
+
uploadScreenshotsForLocale(options: {
|
|
117
|
+
locale: string;
|
|
118
|
+
screenshots: Array<{
|
|
119
|
+
path: string;
|
|
120
|
+
displayType: string;
|
|
121
|
+
filename: string;
|
|
122
|
+
}>;
|
|
123
|
+
}): Promise<{
|
|
124
|
+
uploaded: number;
|
|
125
|
+
deleted: number;
|
|
126
|
+
failed: number;
|
|
127
|
+
}>;
|
|
102
128
|
private getApi;
|
|
103
129
|
private normalizeEndpoint;
|
|
104
130
|
private requestCollection;
|
|
@@ -753,6 +753,130 @@ export class AppStoreClient {
|
|
|
753
753
|
return await this.handleSdkError(error);
|
|
754
754
|
}
|
|
755
755
|
}
|
|
756
|
+
/**
|
|
757
|
+
* Delete a single screenshot
|
|
758
|
+
*/
|
|
759
|
+
async deleteScreenshot(screenshotId) {
|
|
760
|
+
const appScreenshotsApi = await this.getApi(AppScreenshotsApi);
|
|
761
|
+
try {
|
|
762
|
+
await appScreenshotsApi.appScreenshotsDeleteInstance({
|
|
763
|
+
id: screenshotId,
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
catch (error) {
|
|
767
|
+
return await this.handleSdkError(error);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Delete all screenshots in a screenshot set
|
|
772
|
+
*/
|
|
773
|
+
async deleteAllScreenshotsInSet(screenshotSetId) {
|
|
774
|
+
const screenshotsResponse = await this.listScreenshots(screenshotSetId);
|
|
775
|
+
const screenshots = screenshotsResponse.data || [];
|
|
776
|
+
let deletedCount = 0;
|
|
777
|
+
for (const screenshot of screenshots) {
|
|
778
|
+
try {
|
|
779
|
+
await this.deleteScreenshot(screenshot.id);
|
|
780
|
+
deletedCount++;
|
|
781
|
+
}
|
|
782
|
+
catch (error) {
|
|
783
|
+
console.error(`[AppStore] Warning: Failed to delete screenshot ${screenshot.id}: ${error instanceof Error ? error.message : String(error)}`);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
return deletedCount;
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Upload multiple screenshots for a locale, replacing existing ones
|
|
790
|
+
* 1. Find or create screenshot sets for each display type
|
|
791
|
+
* 2. Delete existing screenshots in each set
|
|
792
|
+
* 3. Upload new screenshots in order
|
|
793
|
+
*/
|
|
794
|
+
async uploadScreenshotsForLocale(options) {
|
|
795
|
+
const { locale, screenshots } = options;
|
|
796
|
+
const { readFileSync, statSync, existsSync } = await import("node:fs");
|
|
797
|
+
const { basename } = await import("node:path");
|
|
798
|
+
const result = { uploaded: 0, deleted: 0, failed: 0 };
|
|
799
|
+
if (screenshots.length === 0) {
|
|
800
|
+
return result;
|
|
801
|
+
}
|
|
802
|
+
try {
|
|
803
|
+
// Get app and version info
|
|
804
|
+
const appId = await this.findAppId();
|
|
805
|
+
const versionsResponse = await this.listAppStoreVersions(appId, {
|
|
806
|
+
platform: APP_STORE_PLATFORM,
|
|
807
|
+
limit: DEFAULT_VERSIONS_FETCH_LIMIT,
|
|
808
|
+
});
|
|
809
|
+
const version = sortVersions(versionsResponse.data || [])[0];
|
|
810
|
+
if (!version)
|
|
811
|
+
throw new Error("App Store version not found.");
|
|
812
|
+
// Find or create localization
|
|
813
|
+
const localizationsResponse = await this.listAppStoreVersionLocalizations(version.id, locale);
|
|
814
|
+
let localizationId;
|
|
815
|
+
if (localizationsResponse.data?.[0]) {
|
|
816
|
+
localizationId = localizationsResponse.data[0].id;
|
|
817
|
+
}
|
|
818
|
+
else {
|
|
819
|
+
const createResponse = await this.createAppStoreVersionLocalization(version.id, locale, {});
|
|
820
|
+
localizationId = createResponse.data.id;
|
|
821
|
+
}
|
|
822
|
+
// Group screenshots by display type
|
|
823
|
+
const byDisplayType = new Map();
|
|
824
|
+
for (const screenshot of screenshots) {
|
|
825
|
+
if (!byDisplayType.has(screenshot.displayType)) {
|
|
826
|
+
byDisplayType.set(screenshot.displayType, []);
|
|
827
|
+
}
|
|
828
|
+
byDisplayType.get(screenshot.displayType).push({
|
|
829
|
+
path: screenshot.path,
|
|
830
|
+
filename: screenshot.filename,
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
// Process each display type
|
|
834
|
+
for (const [displayType, screenshotList] of byDisplayType) {
|
|
835
|
+
console.error(`[AppStore] Processing ${displayType} (${screenshotList.length} screenshots)...`);
|
|
836
|
+
// Find or create screenshot set
|
|
837
|
+
const screenshotSetId = await this.findOrCreateScreenshotSet(localizationId, displayType);
|
|
838
|
+
// Delete existing screenshots in this set
|
|
839
|
+
const deletedCount = await this.deleteAllScreenshotsInSet(screenshotSetId);
|
|
840
|
+
if (deletedCount > 0) {
|
|
841
|
+
console.error(`[AppStore] 🗑️ Deleted ${deletedCount} existing screenshots`);
|
|
842
|
+
result.deleted += deletedCount;
|
|
843
|
+
}
|
|
844
|
+
// Upload new screenshots in order
|
|
845
|
+
for (const screenshot of screenshotList) {
|
|
846
|
+
if (!existsSync(screenshot.path)) {
|
|
847
|
+
console.error(`[AppStore] ⚠️ File not found: ${screenshot.filename}`);
|
|
848
|
+
result.failed++;
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
try {
|
|
852
|
+
const fileBuffer = readFileSync(screenshot.path);
|
|
853
|
+
const fileSize = statSync(screenshot.path).size;
|
|
854
|
+
// Create screenshot with upload operation
|
|
855
|
+
const screenshotData = await this.createAppScreenshot(screenshotSetId, screenshot.filename, fileSize);
|
|
856
|
+
// Upload file
|
|
857
|
+
if (screenshotData.uploadOperations &&
|
|
858
|
+
screenshotData.uploadOperations.length > 0) {
|
|
859
|
+
const uploadOp = screenshotData.uploadOperations[0];
|
|
860
|
+
await this.uploadFileToUrl(uploadOp.url, fileBuffer, uploadOp.method);
|
|
861
|
+
}
|
|
862
|
+
// Commit screenshot
|
|
863
|
+
await this.commitAppScreenshot(screenshotData.id);
|
|
864
|
+
console.error(`[AppStore] ✅ ${screenshot.filename}`);
|
|
865
|
+
result.uploaded++;
|
|
866
|
+
}
|
|
867
|
+
catch (error) {
|
|
868
|
+
console.error(`[AppStore] ❌ ${screenshot.filename}: ${error instanceof Error ? error.message : String(error)}`);
|
|
869
|
+
result.failed++;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
return result;
|
|
874
|
+
}
|
|
875
|
+
catch (error) {
|
|
876
|
+
console.error(`[AppStore] Screenshot upload failed for ${locale}: ${error instanceof Error ? error.message : String(error)}`);
|
|
877
|
+
throw error;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
756
880
|
async getApi(apiClass) {
|
|
757
881
|
if (!this.apiCache.has(apiClass)) {
|
|
758
882
|
this.apiCache.set(apiClass, this.sdk.create(apiClass));
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* API Documentation: https://developers.google.com/android-publisher
|
|
6
6
|
*/
|
|
7
7
|
import type { GooglePlayAsoData, GooglePlayMultilingualAsoData, GooglePlayReleaseNote } from "../../../packages/configs/aso-config/types.js";
|
|
8
|
-
import type { GooglePlayClientConfig, AppAccessInfo, LatestReleaseInfo, ReleaseUpdateResult, CreateReleaseOptions, UploadScreenshotOptions, UpdateReleaseNotesOptions, AppDetailsData } from "./types.js";
|
|
8
|
+
import type { GooglePlayClientConfig, AppAccessInfo, LatestReleaseInfo, ReleaseUpdateResult, CreateReleaseOptions, UploadScreenshotOptions, BatchUploadScreenshotsOptions, BatchUploadScreenshotsResult, UpdateReleaseNotesOptions, AppDetailsData } from "./types.js";
|
|
9
9
|
export declare class GooglePlayClient {
|
|
10
10
|
private auth;
|
|
11
11
|
private androidPublisher;
|
|
@@ -49,6 +49,12 @@ export declare class GooglePlayClient {
|
|
|
49
49
|
private updateListing;
|
|
50
50
|
private listImages;
|
|
51
51
|
private uploadImage;
|
|
52
|
+
private deleteAllImages;
|
|
53
|
+
/**
|
|
54
|
+
* Upload multiple screenshots for a locale in a single edit session
|
|
55
|
+
* Deletes existing screenshots before uploading new ones
|
|
56
|
+
*/
|
|
57
|
+
uploadScreenshotsForLocale(options: BatchUploadScreenshotsOptions): Promise<BatchUploadScreenshotsResult>;
|
|
52
58
|
private getTrack;
|
|
53
59
|
private updateTrack;
|
|
54
60
|
private listTracks;
|
|
@@ -606,6 +606,188 @@ export class GooglePlayClient {
|
|
|
606
606
|
}
|
|
607
607
|
return { data: uploadData.image };
|
|
608
608
|
}
|
|
609
|
+
async deleteAllImages(session, language, imageType) {
|
|
610
|
+
await this.androidPublisher.edits.images.deleteall({
|
|
611
|
+
auth: session.auth,
|
|
612
|
+
packageName: session.packageName,
|
|
613
|
+
editId: session.editId,
|
|
614
|
+
language,
|
|
615
|
+
imageType,
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Upload multiple screenshots for a locale in a single edit session
|
|
620
|
+
* Deletes existing screenshots before uploading new ones
|
|
621
|
+
*/
|
|
622
|
+
async uploadScreenshotsForLocale(options) {
|
|
623
|
+
const { language, phoneScreenshots = [], sevenInchScreenshots = [], tenInchScreenshots = [], featureGraphic, } = options;
|
|
624
|
+
const authClient = await this.auth.getClient();
|
|
625
|
+
const editResponse = await this.createEdit(authClient, this.packageName);
|
|
626
|
+
const editId = editResponse.data.id;
|
|
627
|
+
const session = {
|
|
628
|
+
auth: authClient,
|
|
629
|
+
packageName: this.packageName,
|
|
630
|
+
editId,
|
|
631
|
+
};
|
|
632
|
+
const result = {
|
|
633
|
+
language,
|
|
634
|
+
uploaded: {
|
|
635
|
+
phoneScreenshots: 0,
|
|
636
|
+
sevenInchScreenshots: 0,
|
|
637
|
+
tenInchScreenshots: 0,
|
|
638
|
+
featureGraphic: false,
|
|
639
|
+
},
|
|
640
|
+
};
|
|
641
|
+
try {
|
|
642
|
+
// Delete existing screenshots before uploading
|
|
643
|
+
if (phoneScreenshots.length > 0) {
|
|
644
|
+
console.error(`[GooglePlayClient] Deleting existing phone screenshots for ${language}...`);
|
|
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
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if (sevenInchScreenshots.length > 0) {
|
|
656
|
+
console.error(`[GooglePlayClient] Deleting existing 7-inch screenshots for ${language}...`);
|
|
657
|
+
try {
|
|
658
|
+
await this.deleteAllImages(session, language, "sevenInchScreenshots");
|
|
659
|
+
}
|
|
660
|
+
catch (e) {
|
|
661
|
+
if (e.code !== 404) {
|
|
662
|
+
console.error(`[GooglePlayClient] Warning: Failed to delete 7-inch screenshots: ${e.message}`);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
if (tenInchScreenshots.length > 0) {
|
|
667
|
+
console.error(`[GooglePlayClient] Deleting existing 10-inch screenshots for ${language}...`);
|
|
668
|
+
try {
|
|
669
|
+
await this.deleteAllImages(session, language, "tenInchScreenshots");
|
|
670
|
+
}
|
|
671
|
+
catch (e) {
|
|
672
|
+
if (e.code !== 404) {
|
|
673
|
+
console.error(`[GooglePlayClient] Warning: Failed to delete 10-inch screenshots: ${e.message}`);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
if (featureGraphic) {
|
|
678
|
+
console.error(`[GooglePlayClient] Deleting existing feature graphic for ${language}...`);
|
|
679
|
+
try {
|
|
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
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
// Upload phone screenshots
|
|
689
|
+
for (let i = 0; i < phoneScreenshots.length; i++) {
|
|
690
|
+
const imagePath = phoneScreenshots[i];
|
|
691
|
+
if (!existsSync(imagePath)) {
|
|
692
|
+
console.error(`[GooglePlayClient] Warning: Phone screenshot not found: ${imagePath}`);
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
const imageBuffer = readFileSync(imagePath);
|
|
696
|
+
const fileName = imagePath.split("/").pop() || `phone-${i + 1}.png`;
|
|
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;
|
|
740
|
+
}
|
|
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
|
+
}
|
|
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
|
+
}
|
|
780
|
+
catch (error) {
|
|
781
|
+
console.error(`[GooglePlayClient] Rolling back screenshot upload for ${language}...`);
|
|
782
|
+
try {
|
|
783
|
+
await this.deleteEdit(session);
|
|
784
|
+
}
|
|
785
|
+
catch {
|
|
786
|
+
// Ignore deletion failure
|
|
787
|
+
}
|
|
788
|
+
throw error;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
609
791
|
async getTrack(session, track) {
|
|
610
792
|
const response = await this.androidPublisher.edits.tracks.get({
|
|
611
793
|
auth: session.auth,
|
|
@@ -108,6 +108,31 @@ export interface UploadScreenshotOptions {
|
|
|
108
108
|
imageType: "phoneScreenshots" | "sevenInchScreenshots" | "tenInchScreenshots" | "tvScreenshots" | "wearScreenshots" | "featureGraphic";
|
|
109
109
|
language?: string;
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Batch Upload Screenshots Options
|
|
113
|
+
* Internal type for uploading multiple screenshots in a single edit session
|
|
114
|
+
* Replaces existing screenshots (deletes all before upload)
|
|
115
|
+
*/
|
|
116
|
+
export interface BatchUploadScreenshotsOptions {
|
|
117
|
+
language: string;
|
|
118
|
+
phoneScreenshots?: string[];
|
|
119
|
+
sevenInchScreenshots?: string[];
|
|
120
|
+
tenInchScreenshots?: string[];
|
|
121
|
+
featureGraphic?: string;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Batch Upload Screenshots Result
|
|
125
|
+
* Internal type for tracking batch upload results
|
|
126
|
+
*/
|
|
127
|
+
export interface BatchUploadScreenshotsResult {
|
|
128
|
+
language: string;
|
|
129
|
+
uploaded: {
|
|
130
|
+
phoneScreenshots: number;
|
|
131
|
+
sevenInchScreenshots: number;
|
|
132
|
+
tenInchScreenshots: number;
|
|
133
|
+
featureGraphic: boolean;
|
|
134
|
+
};
|
|
135
|
+
}
|
|
111
136
|
/**
|
|
112
137
|
* Update Release Notes Options
|
|
113
138
|
* Internal type for updating release notes
|