@yetech/estra7ah 3.3.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/estra7ah.js +99 -26
  2. package/package.json +1 -1
package/dist/estra7ah.js CHANGED
@@ -36,6 +36,102 @@ function getRunner() {
36
36
  throw new Error("Unsupported platform");
37
37
  }
38
38
 
39
+ // The VPS caps throughput per TCP connection (~2.5 MB/s), not in total, so
40
+ // pulling the binary over several range requests at once is ~N times faster.
41
+ const PARALLEL_CHUNKS = 6;
42
+
43
+ function downloadRange(url, fd, start, end, progressBar, attempt = 0) {
44
+ return axios({
45
+ method: "get",
46
+ url,
47
+ responseType: "stream",
48
+ headers: { Range: `bytes=${start}-${end}` },
49
+ timeout: 0,
50
+ })
51
+ .then(
52
+ (response) =>
53
+ new Promise((resolve, reject) => {
54
+ let position = start;
55
+ response.data.on("data", (chunk) => {
56
+ fs.writeSync(fd, chunk, 0, chunk.length, position);
57
+ position += chunk.length;
58
+ progressBar.tick(chunk.length);
59
+ });
60
+ response.data.on("end", () => resolve(position));
61
+ response.data.on("error", (err) => reject({ err, position }));
62
+ })
63
+ )
64
+ .catch((failure) => {
65
+ const resumeAt = failure.position || start;
66
+ if (attempt < 2) return downloadRange(url, fd, resumeAt, end, progressBar, attempt + 1);
67
+ throw failure.err || failure;
68
+ });
69
+ }
70
+
71
+ async function downloadFileFast(url, destPath) {
72
+ let totalLength = 0;
73
+ try {
74
+ const head = await axios.head(url, { timeout: 10000 });
75
+ if ((head.headers["accept-ranges"] || "").includes("bytes")) {
76
+ totalLength = parseInt(head.headers["content-length"], 10) || 0;
77
+ }
78
+ } catch (error) {
79
+ // fall through to single-stream download
80
+ }
81
+
82
+ if (!totalLength) return downloadFileStream(url, destPath);
83
+
84
+ const progressBar = new ProgressBar("Downloading [:bar] :percent :etas", {
85
+ width: 40,
86
+ complete: "=",
87
+ incomplete: " ",
88
+ total: totalLength,
89
+ });
90
+
91
+ // Download to a temp file so a failed/interrupted update never corrupts
92
+ // the runner binary we already have.
93
+ const tmpPath = `${destPath}.download`;
94
+ const fd = fs.openSync(tmpPath, "w");
95
+ try {
96
+ fs.ftruncateSync(fd, totalLength);
97
+ const chunkSize = Math.ceil(totalLength / PARALLEL_CHUNKS);
98
+ const jobs = [];
99
+ for (let start = 0; start < totalLength; start += chunkSize) {
100
+ const end = Math.min(start + chunkSize - 1, totalLength - 1);
101
+ jobs.push(downloadRange(url, fd, start, end, progressBar));
102
+ }
103
+ await Promise.all(jobs);
104
+ } catch (error) {
105
+ fs.closeSync(fd);
106
+ fs.unlinkSync(tmpPath);
107
+ throw error;
108
+ }
109
+ fs.closeSync(fd);
110
+ fs.renameSync(tmpPath, destPath);
111
+ }
112
+
113
+ function downloadFileStream(url, destPath) {
114
+ return axios({ method: "get", url, responseType: "stream" }).then((response) => {
115
+ const totalLength = parseInt(response.headers["content-length"], 10);
116
+ const progressBar = totalLength
117
+ ? new ProgressBar("Downloading [:bar] :percent :etas", {
118
+ width: 40,
119
+ complete: "=",
120
+ incomplete: " ",
121
+ total: totalLength,
122
+ })
123
+ : null;
124
+ const writer = fs.createWriteStream(destPath);
125
+ return new Promise((resolve, reject) => {
126
+ if (progressBar) response.data.on("data", (chunk) => progressBar.tick(chunk.length));
127
+ writer.on("error", reject);
128
+ writer.on("finish", resolve);
129
+ response.data.on("error", reject);
130
+ response.data.pipe(writer);
131
+ });
132
+ });
133
+ }
134
+
39
135
  // Download the runner executable
40
136
  async function downloadRunner() {
41
137
  let version = null;
@@ -60,32 +156,9 @@ async function downloadRunner() {
60
156
  const runnerPath = `${DIR_NAME}/${getRunner()}`;
61
157
 
62
158
  try {
63
- const response = await axios({
64
- method: "get",
65
- url: serverFile,
66
- responseType: "stream",
67
- });
68
-
69
- const totalLength = parseInt(response.headers["content-length"], 10);
70
- const progressBar = new ProgressBar("Downloading [:bar] :percent :etas", {
71
- width: 40,
72
- complete: "=",
73
- incomplete: " ",
74
- total: totalLength,
75
- });
76
-
77
- const writer = fs.createWriteStream(runnerPath);
78
-
79
- return new Promise((resolve, reject) => {
80
- response.data.on("data", (chunk) => progressBar.tick(chunk.length));
81
- writer.on("error", reject);
82
- writer.on("finish", () => {
83
- fs.writeFileSync(versionFilePath, version);
84
- console.log("** Finished DOWNLOADING NEW RUNNER VERSION **");
85
- resolve();
86
- });
87
- response.data.pipe(writer);
88
- });
159
+ await downloadFileFast(serverFile, runnerPath);
160
+ fs.writeFileSync(versionFilePath, version);
161
+ console.log("** Finished DOWNLOADING NEW RUNNER VERSION **");
89
162
  } catch (error) {
90
163
  console.error("Error downloading runner:", error.message);
91
164
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yetech/estra7ah",
3
- "version": "3.3.0",
3
+ "version": "3.3.1",
4
4
  "description": "Estra7ah Manager",
5
5
  "author": "Abdullah Al-taheri",
6
6
  "keywords": [