Add a windows11 target that downloads the Windows 11 ISO with headless Chromium
Move the Node.js script and its dependencies into nodejs/ and let download-images.sh call it for the image architecture. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5e58722aca
commit
592c7294c3
6 files changed
+228
-3
No files matched your search
Executable
+112
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env node
|
||||
// Print a signed, temporary download URL for the Windows 11 ISO by driving
|
||||
// Microsoft's download page with headless Chromium.
|
||||
//
|
||||
// Usage: node get-windows-iso-url.js [--arch x64|arm64] [--lang NAME]
|
||||
// Environment:
|
||||
// PLAYWRIGHT_CORE playwright-core directory (default: node_modules here)
|
||||
// CHROME_PATH Chromium executable (default: newest one in ~/.cache/ms-playwright)
|
||||
// The URL expires after about 24 hours and may be bound to the IP that
|
||||
// requested it. Only the URL goes to stdout; logs go to stderr.
|
||||
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
if (args.includes('--help') || args.includes('-h')) {
|
||||
console.log(`Usage: node get-windows-iso-url.js [options]
|
||||
|
||||
Print a signed, temporary download URL for the Windows 11 ISO (only the URL
|
||||
goes to stdout; logs go to stderr).
|
||||
|
||||
Options:
|
||||
--arch <x64|arm64> Architecture, default: the host's (x64 on amd64 hosts)
|
||||
--lang <name> Language, default "English (United States)",
|
||||
for example "Chinese Simplified"
|
||||
-h, --help Show this help
|
||||
|
||||
Environment:
|
||||
PLAYWRIGHT_CORE playwright-core directory (default: node_modules here)
|
||||
CHROME_PATH Chromium executable (default: newest one in
|
||||
~/.cache/ms-playwright)
|
||||
|
||||
Examples:
|
||||
node get-windows-iso-url.js --arch x64 --lang "Chinese Simplified"
|
||||
wget -c "$(node get-windows-iso-url.js 2>/dev/null)"
|
||||
|
||||
The URL expires after about 24 hours and may be bound to the IP that
|
||||
requested it.`);
|
||||
process.exit(0);
|
||||
}
|
||||
const opt = (name, def) => {
|
||||
const i = args.indexOf(name);
|
||||
return i < 0 ? def : (args[i + 1] && !args[i + 1].startsWith('--') ? args[i + 1] : true);
|
||||
};
|
||||
const hostArch = { x64: 'x64', arm64: 'arm64' }[process.arch];
|
||||
const arch = opt('--arch', hostArch);
|
||||
const lang = opt('--lang', 'English (United States)');
|
||||
const pages = { arm64: 'windows11arm64', x64: 'windows11' };
|
||||
if (!pages[arch]) {
|
||||
console.error(arch === undefined ? `unsupported host architecture ${process.arch}: pass --arch x64|arm64` : '--arch must be x64 or arm64');
|
||||
process.exit(2);
|
||||
}
|
||||
const log = (...a) => console.error('[iso]', ...a);
|
||||
|
||||
function findPlaywright() {
|
||||
if (process.env.PLAYWRIGHT_CORE) return process.env.PLAYWRIGHT_CORE;
|
||||
try { return path.dirname(require.resolve('playwright-core/package.json')); } catch {}
|
||||
console.error('playwright-core not found: run npm ci or set PLAYWRIGHT_CORE');
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
function findChrome() {
|
||||
if (process.env.CHROME_PATH) return process.env.CHROME_PATH;
|
||||
const root = path.join(os.homedir(), '.cache/ms-playwright');
|
||||
const dirs = fs.existsSync(root) ? fs.readdirSync(root).filter(d => /^chromium-\d+$/.test(d)).sort() : [];
|
||||
if (!dirs.length) { console.error('Chromium not found: set CHROME_PATH or run npx playwright-core install chromium'); process.exit(2); }
|
||||
return path.join(root, dirs.pop(), 'chrome-linux64/chrome');
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const { chromium } = require(findPlaywright());
|
||||
const browser = await chromium.launch({ executablePath: findChrome(), headless: true });
|
||||
try {
|
||||
const ctx = await browser.newContext({
|
||||
locale: 'en-US',
|
||||
userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36',
|
||||
});
|
||||
const page = await ctx.newPage();
|
||||
|
||||
let api = null;
|
||||
page.on('response', async r => {
|
||||
if (/GetProductDownloadLinks/i.test(r.url())) api = await r.json().catch(() => null);
|
||||
});
|
||||
|
||||
log('opening', pages[arch]);
|
||||
await page.goto(`https://www.microsoft.com/en-us/software-download/${pages[arch]}`, { waitUntil: 'networkidle', timeout: 60000 });
|
||||
await page.click('button:has-text("Accept")', { timeout: 3000 }).catch(() => {});
|
||||
|
||||
// Pick the first real edition (currently the only multi-edition ISO).
|
||||
const edition = await page.$$eval('#product-edition option', o => o.map(x => x.value).find(v => v && v !== 'null'));
|
||||
if (!edition) throw new Error('no edition option found; the page layout may have changed');
|
||||
await page.selectOption('#product-edition', edition);
|
||||
await page.click('#submit-product-edition');
|
||||
|
||||
await page.waitForSelector('#product-languages option:not([value="null"])', { state: 'attached', timeout: 60000 });
|
||||
const langs = await page.$$eval('#product-languages option', o => o.map(x => ({ value: x.value, text: x.text })));
|
||||
const hit = langs.find(l => l.text === lang);
|
||||
if (!hit) throw new Error(`no language "${lang}"; available: ${langs.map(l => l.text).filter(t => t !== 'Choose one').join(', ')}`);
|
||||
await page.selectOption('#product-languages', hit.value);
|
||||
await page.click('#submit-sku');
|
||||
|
||||
await page.waitForSelector('a[href*="prss.microsoft.com"]', { timeout: 60000 });
|
||||
const url = await page.$eval('a[href*="prss.microsoft.com"]', a => a.href);
|
||||
const opts = api && api.ProductDownloadOptions;
|
||||
log(opts ? opts[0].Name : '(API response not captured)', '| expires:', api && api.DownloadExpirationDatetime);
|
||||
console.log(url);
|
||||
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
})().catch(e => { console.error('[iso] failed:', e.message); process.exit(1); });
|
||||
Generated
+28
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "win-iso",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "win-iso",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"playwright-core": "^1.64.1"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.64.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.64.1.tgz",
|
||||
"integrity": "sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "win-iso",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "get-windows-iso-url.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"playwright-core": "^1.64.1"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user