Backed out changeset f594e6f00208 (bug 1940883) for causing crashes in bug 1941164.
[gecko.git] / dom / media / tools / generateGmpJson.py
blob2f91b15ec8174002957bdabc07c89373470bc83f
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 import argparse
6 import hashlib
7 import json
8 import logging
9 import re
10 from urllib.parse import urlparse, urlunparse
12 import requests
15 def fetch_url_for_cdms(cdms, urlParams):
16 any_version = None
17 for cdm in cdms:
18 if "fileName" in cdm:
19 cdm["fileUrl"] = cdm["fileName"].format_map(urlParams)
20 response = requests.get(cdm["fileUrl"], allow_redirects=False)
21 if response.status_code != 302:
22 raise Exception(
23 "{} unexpected status code {}".format(
24 cdm["target"], response.status_code
28 redirectUrl = response.headers["Location"]
29 parsedUrl = urlparse(redirectUrl)
30 if parsedUrl.scheme != "https":
31 raise Exception(
32 "{} expected https scheme '{}'".format(cdm["target"], redirectUrl)
35 sanitizedUrl = urlunparse(
36 (parsedUrl.scheme, parsedUrl.netloc, parsedUrl.path, None, None, None)
39 # Note that here we modify the returned URL from the
40 # component update service because it returns a preferred
41 # server for the caller of the script. This may not match
42 # up with what the end users require. Google has requested
43 # that we instead replace these results with the
44 # edgedl.me.gvt1.com domain/path, which should be location
45 # agnostic.
46 normalizedUrl = re.sub(
47 r"https.+?release2",
48 "https://edgedl.me.gvt1.com/edgedl/release2",
49 sanitizedUrl,
51 if not normalizedUrl:
52 raise Exception(
53 "{} cannot normalize '{}'".format(cdm["target"], sanitizedUrl)
56 # Because some users are unable to resolve *.gvt1.com
57 # URLs, we supply an alternative based on www.google.com.
58 # This should resolve with success more frequently.
59 mirrorUrl = re.sub(
60 r"https.+?release2",
61 "https://www.google.com/dl/release2",
62 sanitizedUrl,
65 version = re.search(r".*?_([\d]+\.[\d]+\.[\d]+\.[\d]+)/", sanitizedUrl)
66 if version is None:
67 raise Exception(
68 "{} cannot extract version '{}'".format(cdm["target"], sanitizedUrl)
70 if any_version is None:
71 any_version = version.group(1)
72 elif version.group(1) != any_version:
73 raise Exception(
74 "{} version {} mismatch {}".format(
75 cdm["target"], version.group(1), any_version
78 cdm["fileName"] = normalizedUrl
79 if mirrorUrl and mirrorUrl != normalizedUrl:
80 cdm["fileNameMirror"] = mirrorUrl
81 return any_version
84 def fetch_data_for_cdms(cdms, urlParams):
85 for cdm in cdms:
86 if "fileName" in cdm:
87 cdm["fileUrl"] = cdm["fileName"].format_map(urlParams)
88 response = requests.get(cdm["fileUrl"])
89 response.raise_for_status()
90 cdm["hashValue"] = hashlib.sha512(response.content).hexdigest()
91 if "fileNameMirror" in cdm:
92 cdm["mirrorUrl"] = cdm["fileNameMirror"].format_map(urlParams)
93 mirrorresponse = requests.get(cdm["mirrorUrl"])
94 mirrorresponse.raise_for_status()
95 mirrorhash = hashlib.sha512(mirrorresponse.content).hexdigest()
96 if cdm["hashValue"] != mirrorhash:
97 raise Exception(
98 "Primary hash {} and mirror hash {} differ",
99 cdm["hashValue"],
100 mirrorhash,
102 cdm["filesize"] = len(response.content)
103 if cdm["filesize"] == 0:
104 raise Exception("Empty response for {target}".format_map(cdm))
107 def generate_json_for_cdms(cdms):
108 cdm_json = ""
109 for cdm in cdms:
110 if "alias" in cdm:
111 cdm_json += (
112 ' "{target}": {{\n'
113 + ' "alias": "{alias}"\n'
114 + " }},\n"
115 ).format_map(cdm)
116 elif "mirrorUrl" in cdm:
117 cdm_json += (
118 ' "{target}": {{\n'
119 + ' "fileUrl": "{fileUrl}",\n'
120 + ' "mirrorUrls": [\n'
121 + ' "{mirrorUrl}"\n'
122 + " ],\n"
123 + ' "filesize": {filesize},\n'
124 + ' "hashValue": "{hashValue}"\n'
125 + " }},\n"
126 ).format_map(cdm)
127 else:
128 cdm_json += (
129 ' "{target}": {{\n'
130 + ' "fileUrl": "{fileUrl}",\n'
131 + ' "mirrorUrls": [],\n'
132 + ' "filesize": {filesize},\n'
133 + ' "hashValue": "{hashValue}"\n'
134 + " }},\n"
135 ).format_map(cdm)
136 return cdm_json[:-2] + "\n"
139 def calculate_gmpopenh264_json(version: str, version_hash: str, url_base: str) -> str:
140 # fmt: off
141 cdms = [
142 {"target": "Darwin_aarch64-gcc3", "fileName": "{url_base}/openh264-macosx64-aarch64-{version}.zip"},
143 {"target": "Darwin_x86_64-gcc3", "fileName": "{url_base}/openh264-macosx64-{version}.zip"},
144 {"target": "Linux_aarch64-gcc3", "fileName": "{url_base}/openh264-linux64-aarch64-{version}.zip"},
145 {"target": "Linux_x86-gcc3", "fileName": "{url_base}/openh264-linux32-{version}.zip"},
146 {"target": "Linux_x86_64-gcc3", "fileName": "{url_base}/openh264-linux64-{version}.zip"},
147 {"target": "Linux_x86_64-gcc3-asan", "alias": "Linux_x86_64-gcc3"},
148 {"target": "WINNT_aarch64-msvc-aarch64", "fileName": "{url_base}/openh264-win64-aarch64-{version}.zip"},
149 {"target": "WINNT_x86-msvc", "fileName": "{url_base}/openh264-win32-{version}.zip"},
150 {"target": "WINNT_x86-msvc-x64", "alias": "WINNT_x86-msvc"},
151 {"target": "WINNT_x86-msvc-x86", "alias": "WINNT_x86-msvc"},
152 {"target": "WINNT_x86_64-msvc", "fileName": "{url_base}/openh264-win64-{version}.zip"},
153 {"target": "WINNT_x86_64-msvc-x64", "alias": "WINNT_x86_64-msvc"},
154 {"target": "WINNT_x86_64-msvc-x64-asan", "alias": "WINNT_x86_64-msvc"},
156 # fmt: on
157 try:
158 fetch_data_for_cdms(cdms, {"url_base": url_base, "version": version_hash})
159 except Exception as e:
160 logging.error("calculate_gmpopenh264_json: could not create JSON due to: %s", e)
161 return ""
162 else:
163 return (
164 "{\n"
165 + ' "hashFunction": "sha512",\n'
166 + ' "name": "OpenH264-{}",\n'.format(version)
167 + ' "schema_version": 1000,\n'
168 + ' "vendors": {\n'
169 + ' "gmp-gmpopenh264": {\n'
170 + ' "platforms": {\n'
171 + generate_json_for_cdms(cdms)
172 + " },\n"
173 + ' "version": "{}"\n'.format(version)
174 + " }\n"
175 + " }\n"
176 + "}"
180 def calculate_widevinecdm_json(version: str, url_base: str) -> str:
181 # fmt: off
182 cdms = [
183 {"target": "Darwin_aarch64-gcc3", "fileName": "{url_base}/{version}-mac-arm64.zip"},
184 {"target": "Darwin_x86_64-gcc3", "alias": "Darwin_x86_64-gcc3-u-i386-x86_64"},
185 {"target": "Darwin_x86_64-gcc3-u-i386-x86_64", "fileName": "{url_base}/{version}-mac-x64.zip"},
186 {"target": "Linux_x86_64-gcc3", "fileName": "{url_base}/{version}-linux-x64.zip"},
187 {"target": "Linux_x86_64-gcc3-asan", "alias": "Linux_x86_64-gcc3"},
188 {"target": "WINNT_aarch64-msvc-aarch64", "fileName": "{url_base}/{version}-win-arm64.zip"},
189 {"target": "WINNT_x86-msvc", "fileName": "{url_base}/{version}-win-x86.zip"},
190 {"target": "WINNT_x86-msvc-x64", "alias": "WINNT_x86-msvc"},
191 {"target": "WINNT_x86-msvc-x86", "alias": "WINNT_x86-msvc"},
192 {"target": "WINNT_x86_64-msvc", "fileName": "{url_base}/{version}-win-x64.zip"},
193 {"target": "WINNT_x86_64-msvc-x64", "alias": "WINNT_x86_64-msvc"},
194 {"target": "WINNT_x86_64-msvc-x64-asan", "alias": "WINNT_x86_64-msvc"},
196 # fmt: on
197 try:
198 fetch_data_for_cdms(cdms, {"url_base": url_base, "version": version})
199 except Exception as e:
200 logging.error("calculate_widevinecdm_json: could not create JSON due to: %s", e)
201 return ""
202 else:
203 return (
204 "{\n"
205 + ' "hashFunction": "sha512",\n'
206 + ' "name": "Widevine-{}",\n'.format(version)
207 + ' "schema_version": 1000,\n'
208 + ' "vendors": {\n'
209 + ' "gmp-widevinecdm": {\n'
210 + ' "platforms": {\n'
211 + generate_json_for_cdms(cdms)
212 + " },\n"
213 + ' "version": "{}"\n'.format(version)
214 + " }\n"
215 + " }\n"
216 + "}"
220 def calculate_chrome_component_json(
221 name: str, altname: str, url_base: str, cdms
222 ) -> str:
223 try:
224 version = fetch_url_for_cdms(cdms, {"url_base": url_base})
225 fetch_data_for_cdms(cdms, {})
226 except Exception as e:
227 logging.error(
228 "calculate_chrome_component_json: could not create JSON due to: %s", e
230 return ""
231 else:
232 return (
233 "{\n"
234 + ' "hashFunction": "sha512",\n'
235 + ' "name": "{}-{}",\n'.format(name, version)
236 + ' "schema_version": 1000,\n'
237 + ' "vendors": {\n'
238 + ' "gmp-{}": {{\n'.format(altname)
239 + ' "platforms": {\n'
240 + generate_json_for_cdms(cdms)
241 + " },\n"
242 + ' "version": "{}"\n'.format(version)
243 + " }\n"
244 + " }\n"
245 + "}"
249 def calculate_widevinecdm_component_json(url_base: str) -> str:
250 # fmt: off
251 cdms = [
252 {"target": "Darwin_aarch64-gcc3", "fileName": "{url_base}&os=mac&arch=arm64&os_arch=arm64"},
253 {"target": "Darwin_x86_64-gcc3", "alias": "Darwin_x86_64-gcc3-u-i386-x86_64"},
254 {"target": "Darwin_x86_64-gcc3-u-i386-x86_64", "fileName": "{url_base}&os=mac&arch=x64&os_arch=x64"},
255 {"target": "Linux_x86_64-gcc3", "fileName": "{url_base}&os=Linux&arch=x64&os_arch=x64"},
256 {"target": "Linux_x86_64-gcc3-asan", "alias": "Linux_x86_64-gcc3"},
257 {"target": "WINNT_aarch64-msvc-aarch64", "fileName": "{url_base}&os=win&arch=arm64&os_arch=arm64"},
258 {"target": "WINNT_x86-msvc", "fileName": "{url_base}&os=win&arch=x86&os_arch=x86"},
259 {"target": "WINNT_x86-msvc-x64", "alias": "WINNT_x86-msvc"},
260 {"target": "WINNT_x86-msvc-x86", "alias": "WINNT_x86-msvc"},
261 {"target": "WINNT_x86_64-msvc", "fileName": "{url_base}&os=win&arch=x64&os_arch=x64"},
262 {"target": "WINNT_x86_64-msvc-x64", "alias": "WINNT_x86_64-msvc"},
263 {"target": "WINNT_x86_64-msvc-x64-asan", "alias": "WINNT_x86_64-msvc"},
265 # fmt: on
266 return calculate_chrome_component_json(
267 "Widevine",
268 "widevinecdm",
269 url_base.format_map({"guid": "oimompecagnajdejgnnjijobebaeigek"}),
270 cdms,
274 def calculate_widevinecdm_l1_component_json(url_base: str) -> str:
275 # fmt: off
276 cdms = [
277 {"target": "WINNT_x86_64-msvc", "fileName": "{url_base}&os=win&arch=x64&os_arch=x64"},
278 {"target": "WINNT_x86_64-msvc-x64", "alias": "WINNT_x86_64-msvc"},
279 {"target": "WINNT_x86_64-msvc-x64-asan", "alias": "WINNT_x86_64-msvc"},
281 # fmt: on
282 return calculate_chrome_component_json(
283 "Widevine-L1",
284 "widevinecdm-l1",
285 url_base.format_map({"guid": "neifaoindggfcjicffkgpmnlppeffabd"}),
286 cdms,
290 def main():
291 examples = """examples:
292 python dom/media/tools/generateGmpJson.py widevine 4.10.2557.0 >toolkit/content/gmp-sources/widevinecdm.json
293 python dom/media/tools/generateGmpJson.py --url http://localhost:8080 openh264 2.3.1 0a48f4d2e9be2abb4fb01b4c3be83cf44ce91a6e
294 python dom/media/tools/generateGmpJson.py widevine_component"""
296 parser = argparse.ArgumentParser(
297 description="Generate JSON for GMP plugin updates",
298 epilog=examples,
299 formatter_class=argparse.RawDescriptionHelpFormatter,
301 parser.add_argument(
302 "plugin",
303 help="which plugin: openh264, widevine, widevine_component, widevine_l1_component",
305 parser.add_argument("version", help="version of plugin", nargs="?")
306 parser.add_argument("revision", help="revision hash of plugin", nargs="?")
307 parser.add_argument("--url", help="override base URL from which to fetch plugins")
308 parser.add_argument(
309 "--testrequest",
310 action="store_true",
311 help="request upcoming version for component update service",
313 args = parser.parse_args()
315 if args.plugin == "openh264":
316 url_base = "http://ciscobinary.openh264.org"
317 if args.version is None or args.revision is None:
318 parser.error("openh264 requires version and revision")
319 elif args.plugin == "widevine":
320 url_base = "https://redirector.gvt1.com/edgedl/widevine-cdm"
321 if args.version is None:
322 parser.error("widevine requires version")
323 if args.revision is not None:
324 parser.error("widevine cannot use revision")
325 elif args.plugin in ("widevine_component", "widevine_l1_component"):
326 url_base = "https://update.googleapis.com/service/update2/crx?response=redirect&x=id%3D{guid}%26uc&acceptformat=crx3&updaterversion=999"
327 if args.testrequest:
328 url_base += "&testrequest=1"
329 if args.version is not None or args.revision is not None:
330 parser.error("chrome component cannot use version or revision")
331 else:
332 parser.error("plugin not recognized")
334 if args.url is not None:
335 url_base = args.url
337 if url_base[-1] == "/":
338 url_base = url_base[:-1]
340 if args.plugin == "openh264":
341 json_result = calculate_gmpopenh264_json(args.version, args.revision, url_base)
342 elif args.plugin == "widevine":
343 json_result = calculate_widevinecdm_json(args.version, url_base)
344 elif args.plugin == "widevine_component":
345 json_result = calculate_widevinecdm_component_json(url_base)
346 elif args.plugin == "widevine_l1_component":
347 json_result = calculate_widevinecdm_l1_component_json(url_base)
349 try:
350 json.loads(json_result)
351 except json.JSONDecodeError as e:
352 logging.error("invalid JSON produced: %s", e)
353 else:
354 print(json_result)
357 main()