DownloaderBase.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import { dirname, basename, resolve } from 'path';
  2. import * as request from 'request';
  3. import * as ProgressBar from 'progress';
  4. import { ensureDirSync, exists, lstatAsync, writeFile } from 'fs-extra-promise';
  5. const debug = require('debug')('build:downloader');
  6. const progress = require('request-progress');
  7. import { Event } from './Event';
  8. import { mergeOptions, extractGeneric } from '../util';
  9. const DIR_CACHES = resolve(dirname(module.filename), '..', '..', '..', 'caches');
  10. ensureDirSync(DIR_CACHES);
  11. interface IRequestProgress {
  12. percent: number;
  13. speed: number;
  14. size: {
  15. total: number,
  16. transferred: number,
  17. };
  18. time: {
  19. elapsed: number,
  20. remaining: number,
  21. };
  22. }
  23. export abstract class DownloaderBase {
  24. public onProgress: Event<IRequestProgress> = new Event('progress');
  25. protected destination: string = DIR_CACHES;
  26. public abstract async fetch(): Promise<string>;
  27. protected abstract handleVersion(version: string): Promise<string>;
  28. public async fetchAndExtract() {
  29. const archive = await this.fetch();
  30. const dest = `${ archive }-extracted`;
  31. await extractGeneric(archive, dest);
  32. return dest;
  33. }
  34. protected getVersions(): Promise<any> {
  35. return new Promise((resolve, reject) => {
  36. request('https://nwjs.io/versions.json', (err, res, body) => {
  37. if(err) {
  38. return reject(err);
  39. }
  40. const json = JSON.parse(body);
  41. resolve(json);
  42. });
  43. });
  44. }
  45. protected setDestination(destination: string) {
  46. this.destination = destination;
  47. }
  48. protected handlePlatform(platform: string) {
  49. switch(platform) {
  50. case 'win32':
  51. case 'win':
  52. return 'win';
  53. case 'darwin':
  54. case 'osx':
  55. case 'mac':
  56. return 'osx';
  57. case 'linux':
  58. return 'linux';
  59. default:
  60. throw new Error('ERROR_UNKNOWN_PLATFORM');
  61. }
  62. }
  63. protected handleArch(arch: string) {
  64. switch(arch) {
  65. case 'x86':
  66. case 'ia32':
  67. return 'ia32';
  68. case 'x64':
  69. return 'x64';
  70. default:
  71. throw new Error('ERROR_UNKNOWN_PLATFORM');
  72. }
  73. }
  74. protected getLocalSize(path: string): Promise<number> {
  75. return lstatAsync(path)
  76. .then(stat => stat.size);
  77. }
  78. protected getRemoteSize(url: string): Promise<number> {
  79. return new Promise((resolve, reject) => {
  80. request.head(url)
  81. .on('error', reject)
  82. .on('response', res => resolve(parseInt(res.headers['content-length'], 10)));
  83. });
  84. }
  85. protected isFileExists(path: string) {
  86. return new Promise((resolve, reject) => {
  87. exists(path, resolve);
  88. });
  89. }
  90. protected async isFileSynced(url: string, path: string) {
  91. const localSize = await this.getLocalSize(path);
  92. const remoteSize = await this.getRemoteSize(url);
  93. debug('in isFileSynced', 'localSize', localSize);
  94. debug('in isFileSynced', 'remoteSize', remoteSize);
  95. return localSize == remoteSize;
  96. }
  97. protected async download(url: string, filename: string, path: string, showProgress: boolean) {
  98. let bar: ProgressBar = null;
  99. const onProgress = (state: IRequestProgress) => {
  100. if(!state.size.total) {
  101. return;
  102. }
  103. if(!bar) {
  104. bar = new ProgressBar('[:bar] :speedKB/s :etas', {
  105. width: 50,
  106. total: state.size.total,
  107. });
  108. console.info('');
  109. }
  110. bar.update(state.size.transferred / state.size.total, {
  111. speed: (state.speed / 1000).toFixed(2),
  112. });
  113. };
  114. if(showProgress) {
  115. this.onProgress.subscribe(onProgress);
  116. }
  117. debug('in download', 'start downloading', filename);
  118. await new Promise((resolve, reject) => {
  119. progress(request(url, {
  120. encoding: null,
  121. }, (err, res, data) => {
  122. if(err) {
  123. return reject(err);
  124. }
  125. if(res.statusCode != 200) {
  126. const e = new Error(`ERROR_STATUS_CODE statusCode = ${ res.statusCode }`);
  127. return reject(e);
  128. }
  129. writeFile(path, data, err => err ? reject(err) : resolve());
  130. }))
  131. .on('progress', (state: IRequestProgress) => {
  132. this.onProgress.trigger(state);
  133. });
  134. });
  135. debug('in fetch', 'end downloading', filename);
  136. if(showProgress) {
  137. this.onProgress.unsubscribe(onProgress);
  138. if(bar) {
  139. console.info('');
  140. bar.terminate();
  141. }
  142. }
  143. return path;
  144. }
  145. }