Skip to content

Commit f9517e7

Browse files
committed
TODO: Axios uses xhr instead of http backend. Very unstable hacky solution here as backup.
1 parent fafb6fa commit f9517e7

7 files changed

Lines changed: 227 additions & 163 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"lint": "eslint .",
3838
"napi-build": "cd napi && npm run build && node ../scripts/moveNapi.js",
3939
"nsfw-build": "cd node_modules/nsfw && node-gyp build && cd ../.. && node scripts/moveNsfw.js",
40-
"start-electron:dev": "cross-env-shell NODE_ENV=development APP_TYPE=electron BROWSER=none wait-on http://localhost:8080 -i 2000 && electron .",
40+
"start-electron:dev": "cross-env-shell NODE_ENV=development APP_TYPE=electron wait-on http://localhost:8080 -i 2000 && electron .",
4141
"start-electron:prod": "electron .",
4242
"start-web:dev": "cross-env-shell REACT_APP_RELEASE_TYPE=setup webpack serve --hot --mode development",
4343
"build-web:setup": "cross-env-shell REACT_APP_RELEASE_TYPE=setup webpack --mode production",

public/downloadFile.js

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import makeDir from 'make-dir';
2+
import fss from 'fs';
3+
import axios from 'axios';
4+
import pMap from 'p-map';
5+
import path from 'path';
6+
import adapter from 'axios/lib/adapters/http';
7+
8+
const fs = fss.promises;
9+
10+
function getUri(url) {
11+
return new URL(url).href;
12+
}
13+
14+
export const downloadInstanceFiles = async (
15+
mainWindow,
16+
arr,
17+
updatePercentage,
18+
threads = 4
19+
) => {
20+
let downloaded = 0;
21+
await pMap(
22+
arr,
23+
async item => {
24+
let counter = 0;
25+
let res = false;
26+
if (!item.path || !item.url) {
27+
console.warn('Skipping', item);
28+
return;
29+
}
30+
do {
31+
counter += 1;
32+
if (counter !== 1) {
33+
await new Promise(resolve => setTimeout(resolve, 5000));
34+
}
35+
36+
try {
37+
res = await downloadFileInstance(
38+
item.path,
39+
item.url,
40+
item.sha1,
41+
item.legacyPath
42+
);
43+
} catch {
44+
// Do nothing
45+
}
46+
} while (!res && counter < 3);
47+
downloaded += 1;
48+
if (
49+
updatePercentage &&
50+
(downloaded % 5 === 0 || downloaded === arr.length)
51+
) {
52+
mainWindow.webContents.send(
53+
'download-instance-files-progress',
54+
downloaded
55+
);
56+
}
57+
},
58+
{ concurrency: threads }
59+
);
60+
};
61+
62+
const computeFileHash = (filePath, algorithm = 'sha1') =>
63+
new Promise((resolve, reject) => {
64+
const hash = crypto.createHash(algorithm);
65+
fs.createReadStream(filePath)
66+
.on('data', data => hash.update(data))
67+
.on('end', () => resolve(hash.digest('hex')))
68+
.on('error', reject);
69+
});
70+
71+
const downloadFileInstance = async (fileName, url, sha1, legacyPath) => {
72+
let encodedUrl;
73+
try {
74+
const filePath = path.dirname(fileName);
75+
try {
76+
await fs.access(fileName);
77+
if (legacyPath) await fs.access(legacyPath);
78+
const checksum = await computeFileHash(fileName);
79+
const legacyChecksum = legacyPath && (await computeFileHash(legacyPath));
80+
if (checksum === sha1 && (!legacyPath || legacyChecksum === sha1)) {
81+
return true;
82+
}
83+
} catch {
84+
await makeDir(filePath);
85+
if (legacyPath) await makeDir(path.dirname(legacyPath));
86+
}
87+
88+
encodedUrl = getUri(url);
89+
90+
const { data } = await axios.get(encodedUrl, {
91+
responseType: 'stream',
92+
responseEncoding: null,
93+
adapter,
94+
timeout: 60000 * 20
95+
});
96+
97+
const wStream = fss.createWriteStream(fileName, {
98+
encoding: null
99+
});
100+
101+
data.pipe(wStream);
102+
let wStreamLegacy;
103+
if (legacyPath) {
104+
wStreamLegacy = fss.createWriteStream(legacyPath, {
105+
encoding: null
106+
});
107+
data.pipe(wStreamLegacy);
108+
}
109+
110+
await new Promise((resolve, reject) => {
111+
data.on('error', err => {
112+
console.error(err);
113+
reject(err);
114+
});
115+
116+
data.on('end', () => {
117+
wStream.end();
118+
wStream.close();
119+
if (legacyPath) {
120+
wStreamLegacy.end();
121+
wStreamLegacy.close();
122+
}
123+
resolve();
124+
});
125+
});
126+
return true;
127+
} catch (e) {
128+
console.error(
129+
`Error while downloading <${url} | ${encodedUrl}> to <${fileName}> --> ${e.message}`
130+
);
131+
return false;
132+
}
133+
};
134+
135+
export const downloadFile = async (mainWindow, fileName, url, onProgress) => {
136+
await makeDir(path.dirname(fileName));
137+
138+
const encodedUrl = getUri(url);
139+
140+
const { data, headers } = await axios.get(encodedUrl, {
141+
responseType: 'stream',
142+
responseEncoding: null,
143+
adapter,
144+
timeout: 60000 * 20
145+
});
146+
147+
const out = fss.createWriteStream(fileName, { encoding: null });
148+
data.pipe(out);
149+
150+
// Save variable to know progress
151+
let receivedBytes = 0;
152+
const totalBytes = parseInt(headers['content-length'], 10);
153+
154+
data.on('data', chunk => {
155+
// Update the received bytes
156+
receivedBytes += chunk.length;
157+
if (onProgress) {
158+
mainWindow.webContents.send(
159+
'download-file-progress',
160+
parseInt(((receivedBytes * 100) / totalBytes).toFixed(1), 10)
161+
);
162+
}
163+
});
164+
165+
return new Promise((resolve, reject) => {
166+
data.on('end', () => {
167+
out.end();
168+
out.close();
169+
resolve();
170+
});
171+
172+
data.on('error', () => {
173+
reject();
174+
});
175+
});
176+
};

public/electron.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ let tray;
3535
let watcher;
3636

3737
const discordRPC = require('./discordRPC');
38+
const { downloadInstanceFiles, downloadFile } = require('./downloadFile');
3839

3940
const gotTheLock = app.requestSingleInstanceLock();
4041

@@ -901,6 +902,24 @@ ipcMain.handle('download-optedout-mods', async (e, { mods, instancePath }) => {
901902
}
902903
});
903904

905+
ipcMain.handle(
906+
'download-instance-files',
907+
async (e, arr, updatePercentage, threads) => {
908+
const back = downloadInstanceFiles(
909+
mainWindow,
910+
arr,
911+
updatePercentage,
912+
threads
913+
);
914+
log.log('Back:', back);
915+
return back;
916+
}
917+
);
918+
919+
ipcMain.handle('download-file', async (e, fileName, url, onProgress) => {
920+
return downloadFile(mainWindow, fileName, url, onProgress);
921+
});
922+
904923
ipcMain.handle('start-listener', async (e, dirPath) => {
905924
try {
906925
log.log('Trying to start listener');

src/app/desktop/DesktopRoot.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { ipcRenderer } from 'electron';
66
import { useSelector, useDispatch } from 'react-redux';
77
import { push } from 'connected-react-router';
88
import { message } from 'antd';
9+
import axios from 'axios';
10+
import adapter from 'axios/lib/adapters/http';
911
import RouteWithSubRoutes from '../../common/components/RouteWithSubRoutes';
1012
import {
1113
loginWithAccessToken,
@@ -72,6 +74,11 @@ function DesktopRoot({ store }) {
7274
});
7375

7476
const init = async () => {
77+
console.log(axios.defaults.adapter);
78+
axios.defaults.adapter = adapter;
79+
console.log(axios.defaults.adapter);
80+
console.log(adapter);
81+
7582
dispatch(requesting(features.mcAuthentication));
7683
const userDataStatic = await ipcRenderer.invoke('getUserData');
7784
const userData = dispatch(updateUserData(userDataStatic));

src/app/desktop/utils/computeFileHash.js

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)