Add command to download device images
This commit is contained in:
parent
306695fb09
commit
03db6352bd
3 changed files with 88 additions and 1 deletions
|
@ -13,6 +13,7 @@
|
||||||
"@oclif/plugin-help": "^3",
|
"@oclif/plugin-help": "^3",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"cli-progress": "^3.9.1",
|
"cli-progress": "^3.9.1",
|
||||||
|
"node-fetch": "2",
|
||||||
"ora": "5.4.1",
|
"ora": "5.4.1",
|
||||||
"tslib": "^1",
|
"tslib": "^1",
|
||||||
"zx": "^4.2.0"
|
"zx": "^4.2.0"
|
||||||
|
|
86
src/commands/download.ts
Normal file
86
src/commands/download.ts
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
import {Command, flags} from '@oclif/command'
|
||||||
|
import {createWriteStream, promises as fs} from 'fs'
|
||||||
|
import * as chalk from 'chalk'
|
||||||
|
import * as cliProgress from 'cli-progress'
|
||||||
|
import fetch from 'node-fetch'
|
||||||
|
import {promises as stream} from 'stream'
|
||||||
|
import * as path from 'path'
|
||||||
|
|
||||||
|
const VENDOR_INDEX_URL = 'https://developers.google.com/android/drivers'
|
||||||
|
const VENDOR_URL_PREFIX = 'https://dl.google.com/dl/android/aosp/google_devices'
|
||||||
|
|
||||||
|
async function getUrl(type: string, buildId: string, device: string) {
|
||||||
|
if (type == 'vendor') {
|
||||||
|
let resp = await fetch(VENDOR_INDEX_URL)
|
||||||
|
let index = await resp.text()
|
||||||
|
|
||||||
|
// TODO: parse HTML properly
|
||||||
|
let pattern = new RegExp(`"(${VENDOR_URL_PREFIX}-${device}-${buildId.toLowerCase()}-[a-z9-9.-_]+)">`)
|
||||||
|
let match = index.match(pattern)
|
||||||
|
if (match == null) {
|
||||||
|
throw new Error(`Image not found: ${type}, ${buildId}, ${device}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return match[1]
|
||||||
|
} else {
|
||||||
|
// TODO: implement factory and ota
|
||||||
|
throw new Error(`Unsupported type ${type}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function downloadFile(type: string, buildId: string, device: string, outDir: string) {
|
||||||
|
let url = await getUrl(type, buildId, device)
|
||||||
|
|
||||||
|
let resp = await fetch(url)
|
||||||
|
let name = path.basename(url)
|
||||||
|
if (!resp.ok) {
|
||||||
|
throw new Error(`Error ${resp.status}: ${resp.statusText}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
let bar = new cliProgress.SingleBar({
|
||||||
|
format: ' {bar} {percentage}% | {value}/{total} MB',
|
||||||
|
}, cliProgress.Presets.shades_classic)
|
||||||
|
let progress = 0
|
||||||
|
let totalSize = parseInt(resp.headers.get('content-length') ?? '0') / 1e6
|
||||||
|
bar.start(Math.round(totalSize), 0)
|
||||||
|
resp.body!.on('data', chunk => {
|
||||||
|
progress += chunk.length / 1e6
|
||||||
|
bar.update(Math.round(progress))
|
||||||
|
})
|
||||||
|
|
||||||
|
await stream.pipeline(resp.body!, createWriteStream(`${outDir}/${name}`))
|
||||||
|
bar.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Download extends Command {
|
||||||
|
static description = 'download device factory images, OTAs, and/or vendor packages'
|
||||||
|
|
||||||
|
static flags = {
|
||||||
|
help: flags.help({char: 'h'}),
|
||||||
|
type: flags.string({char: 't', options: ['factory', 'ota', 'vendor'], description: 'type(s) of images to download', default: 'factory', multiple: true}),
|
||||||
|
buildId: flags.string({char: 'b', description: 'build ID/number of the image(s) to download', required: true}),
|
||||||
|
device: flags.string({char: 'd', description: 'device(s) to download images for', required: true, multiple: true}),
|
||||||
|
}
|
||||||
|
|
||||||
|
static args = [
|
||||||
|
{name: 'out', description: 'directory to save downloaded files in', required: true},
|
||||||
|
]
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
let {flags, args: {out}} = this.parse(Download)
|
||||||
|
|
||||||
|
fs.mkdir(out, { recursive: true })
|
||||||
|
|
||||||
|
let buildId = flags.buildId.toUpperCase()
|
||||||
|
|
||||||
|
for (let type of flags.type) {
|
||||||
|
let prettyType = type == 'ota' ? 'OTA' : type.charAt(0).toUpperCase() + type.slice(1)
|
||||||
|
this.log(chalk.bold(chalk.blueBright(`${prettyType} - ${buildId}`)))
|
||||||
|
|
||||||
|
for (let device of flags.device) {
|
||||||
|
this.log(chalk.greenBright(` ${device}`))
|
||||||
|
await downloadFile(type, buildId, device, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2347,7 +2347,7 @@ nock@^13.0.0:
|
||||||
lodash.set "^4.3.2"
|
lodash.set "^4.3.2"
|
||||||
propagate "^2.0.0"
|
propagate "^2.0.0"
|
||||||
|
|
||||||
node-fetch@^2.6.1:
|
node-fetch@2, node-fetch@^2.6.1:
|
||||||
version "2.6.6"
|
version "2.6.6"
|
||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
|
||||||
integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==
|
integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==
|
||||||
|
|
Loading…
Reference in a new issue