-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsync_android_metadata.py
More file actions
177 lines (148 loc) · 6.5 KB
/
Copy pathsync_android_metadata.py
File metadata and controls
177 lines (148 loc) · 6.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#!/usr/bin/env python3
"""Upload Android store listing metadata to Google Play using the Developer API.
Bypasses fastlane supply's track/release resolution which fails when
multiple releases exist on a track. Store listing metadata (title,
short description, full description) is app-level, not track-specific.
Usage:
python3 scripts/sync_android_metadata.py
Requires:
- GOOGLE_PLAY_JSON_KEY_PATH or $RUNNER_TEMP/play-service-account.json
- google-api-python-client, google-auth
"""
import json
import os
import sys
from pathlib import Path
PACKAGE_NAME = "com.iganapolsky.randomtimer"
METADATA_ROOT = Path(__file__).resolve().parent.parent / "native-android" / "fastlane" / "metadata" / "android"
# Local dir name -> Google Play API language code.
# ja-JP/ko: Enable locale in Play Console first; per-locale errors are caught and skipped.
LANG_MAP = {
"en-US": "en-US",
"de-DE": "de-DE",
"pt-BR": "pt-BR",
"ja-JP": "ja-JP",
"ko": "ko-KR",
}
def read_metadata(lang_dir: str, filename: str) -> str:
path = METADATA_ROOT / lang_dir / filename
if path.exists():
return path.read_text().strip()
return ""
def build_edits_service(key_path: str):
from google.oauth2 import service_account
from googleapiclient.discovery import build
credentials = service_account.Credentials.from_service_account_file(
key_path,
scopes=["https://www.googleapis.com/auth/androidpublisher"],
)
service = build("androidpublisher", "v3", credentials=credentials)
return service.edits()
def commit_edit(edits, *, edit_id: str):
"""Commit listing edit. Omit changesNotSentForReview: Play rejects it for apps on auto-review."""
return edits.commit(packageName=PACKAGE_NAME, editId=edit_id).execute()
def main():
key_path = os.environ.get("GOOGLE_PLAY_JSON_KEY_PATH", os.path.join(os.environ.get("RUNNER_TEMP", os.getcwd()), "play-service-account.json"))
if not os.path.exists(key_path):
print(f"Service account key not found at {key_path}", file=sys.stderr)
sys.exit(1)
edits = build_edits_service(key_path)
# Create edit
edit = edits.insert(packageName=PACKAGE_NAME, body={}).execute()
edit_id = edit["id"]
print(f"Created edit: {edit_id}")
updated = []
skipped = []
for local_lang, api_lang in LANG_MAP.items():
title = read_metadata(local_lang, "title.txt")
short_desc = read_metadata(local_lang, "short_description.txt")
full_desc = read_metadata(local_lang, "full_description.txt")
if not any([title, short_desc, full_desc]):
continue
listing = {}
if title:
listing["title"] = title
if short_desc:
listing["shortDescription"] = short_desc
if full_desc:
listing["fullDescription"] = full_desc
try:
edits.listings().update(
packageName=PACKAGE_NAME,
editId=edit_id,
language=api_lang,
body=listing,
).execute()
updated.append(api_lang)
print(f" Updated listing for {api_lang}: title={'yes' if title else 'no'}, short={'yes' if short_desc else 'no'}, full={'yes' if full_desc else 'no'}")
except Exception as e:
err_msg = str(e).lower()
if "invalid" in err_msg or "404" in err_msg or "not found" in err_msg:
skipped.append(api_lang)
print(f" Skipped {api_lang}: locale not configured in Play Console or invalid request", file=sys.stderr)
else:
raise
# Upload screenshots for en-US
screenshots_dir = METADATA_ROOT / "en-US" / "images" / "phoneScreenshots"
screenshot_count = 0
if screenshots_dir.is_dir():
pngs = sorted(p for p in screenshots_dir.iterdir() if p.suffix == ".png" and not p.name.startswith("_"))
if pngs:
# Delete existing screenshots first
try:
edits.images().deleteall(
packageName=PACKAGE_NAME,
editId=edit_id,
language="en-US",
imageType="phoneScreenshots",
).execute()
print(" Deleted existing phone screenshots")
except Exception as e:
print(f" Warning: could not delete existing screenshots: {e}", file=sys.stderr)
for png_path in pngs:
try:
from googleapiclient.http import MediaFileUpload
media = MediaFileUpload(str(png_path), mimetype="image/png")
edits.images().upload(
packageName=PACKAGE_NAME,
editId=edit_id,
language="en-US",
imageType="phoneScreenshots",
media_body=media,
).execute()
screenshot_count += 1
print(f" Uploaded screenshot: {png_path.name}")
except Exception as e:
print(f" Warning: failed to upload {png_path.name}: {e}", file=sys.stderr)
# Upload feature graphic
feature_graphic = METADATA_ROOT / "en-US" / "images" / "featureGraphic" / "feature-graphic.png"
if feature_graphic.is_file():
try:
from googleapiclient.http import MediaFileUpload
edits.images().deleteall(
packageName=PACKAGE_NAME,
editId=edit_id,
language="en-US",
imageType="featureGraphic",
).execute()
media = MediaFileUpload(str(feature_graphic), mimetype="image/png")
edits.images().upload(
packageName=PACKAGE_NAME,
editId=edit_id,
language="en-US",
imageType="featureGraphic",
media_body=media,
).execute()
print(" Uploaded feature graphic")
except Exception as e:
print(f" Warning: failed to upload feature graphic: {e}", file=sys.stderr)
if not updated and screenshot_count == 0:
print("No metadata or screenshots to upload. Discarding edit.")
edits.delete(packageName=PACKAGE_NAME, editId=edit_id).execute()
return
if skipped:
print(f"Skipped {len(skipped)} locales (enable in Play Console if needed): {', '.join(skipped)}", file=sys.stderr)
commit_edit(edits, edit_id=edit_id)
print(f"Committed edit. Updated {len(updated)} languages, {screenshot_count} screenshots uploaded.")
if __name__ == "__main__":
main()