@vbarbarosh/node-helpers 3.35.0 → 3.37.0
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.
- package/package.json +1 -1
- package/src/format_bytes.js +3 -3
- package/src/format_bytes.test.js +22 -0
- package/src/parse_bytes.js +14 -0
- package/src/parse_bytes.test.js +21 -0
- package/src/shell_curl_progress.js +35 -0
- package/src/shell_ytdlp_progress.js +8 -1
- package/src/stream_curl_progress.js +46 -0
- package/src/stream_skip.js +25 -0
- package/src/stream_ytdlp_progress.js +25 -4
package/package.json
CHANGED
package/src/format_bytes.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
function format_bytes(bytes)
|
|
2
2
|
{
|
|
3
3
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
|
4
|
-
if (Number.isNaN(bytes
|
|
4
|
+
if (typeof bytes !== 'number' || Number.isNaN(bytes)) {
|
|
5
5
|
return 'n/a';
|
|
6
6
|
}
|
|
7
|
-
if (bytes
|
|
7
|
+
if (!bytes) {
|
|
8
8
|
return '0KB';
|
|
9
9
|
}
|
|
10
10
|
if (bytes < 1024) {
|
|
11
11
|
return '1KB';
|
|
12
12
|
}
|
|
13
13
|
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10);
|
|
14
|
-
return `${(bytes / (1024 ** i)).toFixed(bytes > 1024*1024 ?
|
|
14
|
+
return `${(bytes / (1024 ** i)).toFixed(bytes > 1024*1024 ? 2 : 0)}${sizes[i]}`.replace(/\.00/, '.0');
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
module.exports = format_bytes;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const format_bytes = require('./format_bytes');
|
|
3
|
+
|
|
4
|
+
const items = [
|
|
5
|
+
['', 'n/a'],
|
|
6
|
+
[null, 'n/a'],
|
|
7
|
+
[undefined, 'n/a'],
|
|
8
|
+
[0, '0KB'],
|
|
9
|
+
[1, '1KB'],
|
|
10
|
+
[1023, '1KB'],
|
|
11
|
+
[2048, '2KB'],
|
|
12
|
+
[35.45*1024*1024, '35.45MB'],
|
|
13
|
+
[3.92*1024*1024*1024, '3.92GB'],
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
describe('format_bytes', function () {
|
|
17
|
+
items.forEach(function ([input, expected]) {
|
|
18
|
+
it(`${input} → ${expected}`, function () {
|
|
19
|
+
assert.strictEqual(expected, format_bytes(input));
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function parse_bytes(bytes)
|
|
2
|
+
{
|
|
3
|
+
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
|
4
|
+
if (typeof bytes !== 'string') {
|
|
5
|
+
return Number.NaN;
|
|
6
|
+
}
|
|
7
|
+
const m = bytes.toUpperCase().match(/(\d+([.]?\d+)?)(([KMGTP]I?)?B)/);
|
|
8
|
+
if (!m) {
|
|
9
|
+
return Number.NaN;
|
|
10
|
+
}
|
|
11
|
+
return Math.floor(m[1]*Math.pow(1024, sizes.indexOf(m[3].replace('I', ''))));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = parse_bytes;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const parse_bytes = require('./parse_bytes');
|
|
3
|
+
|
|
4
|
+
const items = [
|
|
5
|
+
['', Number.NaN],
|
|
6
|
+
[null, Number.NaN],
|
|
7
|
+
[undefined, Number.NaN],
|
|
8
|
+
['0KB', 0],
|
|
9
|
+
['1KB', 1024],
|
|
10
|
+
['2KB', 2048],
|
|
11
|
+
['35.45MB', Math.floor(35.45*1024*1024)],
|
|
12
|
+
['3.92GB', Math.floor(3.92*1024*1024*1024)],
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
describe('parse_bytes', function () {
|
|
16
|
+
items.forEach(function ([input, expected]) {
|
|
17
|
+
it(`${input} → ${expected}`, function () {
|
|
18
|
+
assert.strictEqual(expected, parse_bytes(input));
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const Promise = require('bluebird');
|
|
2
|
+
const child_process = require('child_process');
|
|
3
|
+
const stream = require('stream');
|
|
4
|
+
const stream_curl_progress = require('./stream_curl_progress');
|
|
5
|
+
const stream_each = require('./stream_each');
|
|
6
|
+
|
|
7
|
+
async function shell_curl_progress(args, {options, user_friendly_status})
|
|
8
|
+
{
|
|
9
|
+
// $ man curl
|
|
10
|
+
// > If you want a progress meter for HTTP POST or PUT requests,
|
|
11
|
+
// > you need to redirect the response output to a file, using
|
|
12
|
+
// > shell redirect (>), -o, --output or similar.
|
|
13
|
+
|
|
14
|
+
const tmp = ['--progress-meter'];
|
|
15
|
+
if (!args.includes('-o')) {
|
|
16
|
+
tmp.push('-o', '/dev/null');
|
|
17
|
+
}
|
|
18
|
+
const proc = child_process.spawn(args[0], args.slice(1).concat(tmp), {...options, stdio: ['inherit', 'inherit', 'pipe']});
|
|
19
|
+
|
|
20
|
+
const promises = [];
|
|
21
|
+
promises.push(new Promise(function (resolve, reject) {
|
|
22
|
+
proc.once('error', reject);
|
|
23
|
+
proc.once('exit', code => code ? reject(new Error(`Process terminated with code ${code}`)) : resolve());
|
|
24
|
+
}));
|
|
25
|
+
promises.push(stream.promises.pipeline(proc.stderr, stream_curl_progress(), stream_each(progress_fn)));
|
|
26
|
+
|
|
27
|
+
await Promise.all(promises);
|
|
28
|
+
|
|
29
|
+
function progress_fn(v) {
|
|
30
|
+
const speed = v.speed === '0' ? '~' : `${v.speed}/s`;
|
|
31
|
+
user_friendly_status(`${v.perc}% | ${v.done} of ${v.total} at ${speed} ETA ${v.eta} duration=${v.duration}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = shell_curl_progress;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
const child_process = require('child_process');
|
|
2
|
+
const format_seconds = require('./format_seconds');
|
|
2
3
|
const stream = require('stream');
|
|
3
4
|
const stream_each = require('./stream_each');
|
|
4
5
|
const stream_ytdlp_progress = require('./stream_ytdlp_progress');
|
|
5
6
|
|
|
6
|
-
async function shell_ytdlp_progress(args, {
|
|
7
|
+
async function shell_ytdlp_progress(args, {user_friendly_status, ...options})
|
|
7
8
|
{
|
|
8
9
|
const proc = child_process.spawn('yt-dlp', ['--progress', ...args], {...options, stdio: 'pipe'});
|
|
10
|
+
const time0 = Date.now();
|
|
9
11
|
|
|
10
12
|
const promises = [];
|
|
11
13
|
promises.push(new Promise(function (resolve, reject) {
|
|
@@ -16,6 +18,11 @@ async function shell_ytdlp_progress(args, {progress_fn, ...options})
|
|
|
16
18
|
promises.push(stream.promises.pipeline(proc.stderr, stream_each(v => console.log(`[stderr] ${v}`))));
|
|
17
19
|
|
|
18
20
|
await Promise.all(promises);
|
|
21
|
+
|
|
22
|
+
function progress_fn(v) {
|
|
23
|
+
const duration = format_seconds((Date.now() - time0)/1000);
|
|
24
|
+
user_friendly_status(`${v.perc} | [${v.current_part}/${v.total_parts}] ${v.done} of ${v.total} at ${v.speed} ETA ${v.eta} duration=${duration}`);
|
|
25
|
+
}
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
module.exports = shell_ytdlp_progress;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const stream = require('stream');
|
|
2
|
+
const stream_filter = require('./stream_filter');
|
|
3
|
+
const stream_lines = require('./stream_lines');
|
|
4
|
+
const stream_map = require('./stream_map');
|
|
5
|
+
const stream_skip = require('./stream_skip');
|
|
6
|
+
|
|
7
|
+
function stream_curl_progress()
|
|
8
|
+
{
|
|
9
|
+
return stream.compose(
|
|
10
|
+
stream_lines(),
|
|
11
|
+
stream_skip(2),
|
|
12
|
+
stream_map(function (v) {
|
|
13
|
+
// curl: (22) The requested URL returned error: 500
|
|
14
|
+
if (v.startsWith('curl:')) {
|
|
15
|
+
process.stderr.write(`${v}\n`);
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// % Total % Received % Xferd Average Speed Time Time Time Current
|
|
20
|
+
// Dload Upload Total Spent Left Speed
|
|
21
|
+
// 0 3217M 0 0 0 128k 0 205k 4:27:16 --:--:-- 4:27:16 205k
|
|
22
|
+
// 0 3217M 0 0 0 6016k 0 3751k 0:14:38 0:00:01 0:14:37 3750k
|
|
23
|
+
// ...
|
|
24
|
+
// 18 3217M 0 0 18 587M 0 18.5M 0:02:53 0:00:31 0:02:22 19.0M
|
|
25
|
+
const [perc, total, perc2, recv, perc3, transferred, speed_down, speed_up, time_total, duration, eta, speed] = v.trim().split(/\s+/);
|
|
26
|
+
return {
|
|
27
|
+
perc,
|
|
28
|
+
done: recv === '0' ? transferred : recv,
|
|
29
|
+
total,
|
|
30
|
+
perc2,
|
|
31
|
+
recv,
|
|
32
|
+
perc3,
|
|
33
|
+
transferred,
|
|
34
|
+
speed_down,
|
|
35
|
+
speed_up,
|
|
36
|
+
time_total: time_total === '--:--:--' ? '~' : time_total,
|
|
37
|
+
duration: duration === '--:--:--' ? '~' : duration,
|
|
38
|
+
eta: eta === '--:--:--' ? '~' : eta,
|
|
39
|
+
speed
|
|
40
|
+
};
|
|
41
|
+
}),
|
|
42
|
+
stream_filter(v => v),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = stream_curl_progress;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const stream = require('stream');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Skip first `n` records.
|
|
5
|
+
*/
|
|
6
|
+
function stream_skip(n)
|
|
7
|
+
{
|
|
8
|
+
let current = 0;
|
|
9
|
+
return new stream.Transform({
|
|
10
|
+
objectMode: true,
|
|
11
|
+
transform: function (item, encoding, callback) {
|
|
12
|
+
try {
|
|
13
|
+
if (current++ >= n) {
|
|
14
|
+
this.push(item);
|
|
15
|
+
}
|
|
16
|
+
callback();
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
callback(error);
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = stream_skip;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
const format_bytes = require('./format_bytes');
|
|
2
|
+
const format_percents = require('./format_percents');
|
|
3
|
+
const parse_bytes = require('./parse_bytes');
|
|
1
4
|
const stream = require('stream');
|
|
2
5
|
const stream_filter = require('./stream_filter');
|
|
3
6
|
const stream_lines = require('./stream_lines');
|
|
@@ -5,12 +8,30 @@ const stream_map = require('./stream_map');
|
|
|
5
8
|
|
|
6
9
|
function stream_ytdlp_progress()
|
|
7
10
|
{
|
|
8
|
-
|
|
11
|
+
let current_part = 1;
|
|
12
|
+
const downloading_formats = [];
|
|
9
13
|
return stream.compose(
|
|
10
14
|
stream_lines(),
|
|
11
|
-
stream_map(function (
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
stream_map(function (line) {
|
|
16
|
+
let m;
|
|
17
|
+
if (m = line.match(/^\[info] \S+: Downloading \d+ format\(s\): (.+)$/)) {
|
|
18
|
+
downloading_formats.push(...m[1].split('+'));
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (m = line.match(/^\[download] Destination: .*f(\d+)\.[^.]+/)) {
|
|
22
|
+
current_part = downloading_formats.indexOf(m[1]) + 1;
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
if (m = line.match(/^\[download] (.*) of (.*) at (.*) ETA (.*)$/)) {
|
|
26
|
+
const percentage = parseFloat(m[1].trim())/100;
|
|
27
|
+
const total = parse_bytes(m[2].trim());
|
|
28
|
+
const speed = m[3].trim().replace('i', '');
|
|
29
|
+
const eta = m[4].trim();
|
|
30
|
+
return {current_part, total_parts: downloading_formats.length,
|
|
31
|
+
perc: format_percents(percentage), done: format_bytes(percentage*total),
|
|
32
|
+
total: format_bytes(total), speed, eta};
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
14
35
|
}),
|
|
15
36
|
stream_filter(v => v),
|
|
16
37
|
);
|