arcadible 0.1.22 → 0.1.24
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/bin/arc-login.js +2 -2
- package/bin/arc-validate.js +13 -2
- package/package.json +1 -1
- package/src/action/inspect/media.js +2 -12
- package/src/action/login/flow.js +6 -6
- package/src/config/art.js +16 -0
- package/src/config/image.js +8 -0
- package/src/config/video.js +7 -0
- package/src/parser/media.js +6 -13
- package/src/schema/game.js +76 -27
- package/src/schema/workflow.js +3 -3
- package/src/util/bytes.js +20 -0
- package/src/util/image.js +26 -15
- package/src/util/progress.js +5 -15
- package/src/util/schema.js +26 -2
- package/src/util/video.js +26 -14
package/bin/arc-login.js
CHANGED
|
@@ -68,9 +68,9 @@ const onLoginEnter = function (fn) {
|
|
|
68
68
|
|
|
69
69
|
const handler = function ({ browser, qrcode: qr, sw }) {
|
|
70
70
|
return {
|
|
71
|
-
onLoginCreated({ code: dac,
|
|
71
|
+
onLoginCreated({ code: dac, secret, url }) {
|
|
72
72
|
sw.stop(true);
|
|
73
|
-
const link = `${url}/${
|
|
73
|
+
const link = `${url}/${secret}`;
|
|
74
74
|
log.p("Logging in...");
|
|
75
75
|
qrcode.generate(link, { small: true }, (code) => {
|
|
76
76
|
if (qr) {
|
package/bin/arc-validate.js
CHANGED
|
@@ -17,10 +17,12 @@ const cfgproduct = require("../src/config/product");
|
|
|
17
17
|
const log = require("../src/util/log");
|
|
18
18
|
const output = require("../src/util/output");
|
|
19
19
|
const rc = require("../src/util/rc");
|
|
20
|
+
const schema = require("../src/util/schema");
|
|
20
21
|
const validate = require("../src/cli/validate");
|
|
21
22
|
|
|
22
23
|
// [use]
|
|
23
24
|
|
|
25
|
+
const dereference = schema.dereference;
|
|
24
26
|
const findRc = rc.findRc;
|
|
25
27
|
const print = output.print;
|
|
26
28
|
const resolve = path.resolve;
|
|
@@ -49,8 +51,17 @@ const start = function (argv) {
|
|
|
49
51
|
if (json) {
|
|
50
52
|
print({ issues });
|
|
51
53
|
} else if (count) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
const [probs, refs] = dereference(issues);
|
|
55
|
+
const probcount = probs.length;
|
|
56
|
+
if (probcount) {
|
|
57
|
+
log.hd(`${probcount} ${probcount === 1 ? "issue" : "issues"}:`);
|
|
58
|
+
log.p(probs.map((prob) => `✗ ${prob}`));
|
|
59
|
+
}
|
|
60
|
+
const refcount = refs.length;
|
|
61
|
+
if (refcount) {
|
|
62
|
+
log.h(`${refcount} ${refcount === 1 ? "reference" : "references"}:`);
|
|
63
|
+
log.p(refs);
|
|
64
|
+
}
|
|
54
65
|
} else if (!argv.quiet) {
|
|
55
66
|
log.hs("No issues found.");
|
|
56
67
|
}
|
package/package.json
CHANGED
|
@@ -2,22 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
// [import]
|
|
4
4
|
|
|
5
|
+
const cfgart = require("../../config/art");
|
|
5
6
|
const cfgproduct = require("../../config/product");
|
|
6
7
|
|
|
7
8
|
// [declare]
|
|
8
9
|
|
|
9
|
-
const ART = [
|
|
10
|
-
"banner",
|
|
11
|
-
"banner_video",
|
|
12
|
-
"cover",
|
|
13
|
-
"cover_video",
|
|
14
|
-
"icon",
|
|
15
|
-
"icon_video",
|
|
16
|
-
"poster",
|
|
17
|
-
"poster_video",
|
|
18
|
-
"wordmark",
|
|
19
|
-
"wordmark_animated",
|
|
20
|
-
];
|
|
10
|
+
const ART = cfgart.map((file) => file.split(".")[0]);
|
|
21
11
|
|
|
22
12
|
// [declare]
|
|
23
13
|
|
package/src/action/login/flow.js
CHANGED
|
@@ -20,10 +20,10 @@ const randomUUID = crypto.randomUUID;
|
|
|
20
20
|
// [declare]
|
|
21
21
|
|
|
22
22
|
const POLL = {
|
|
23
|
-
delay: time.s(
|
|
24
|
-
factor:
|
|
25
|
-
interval: time.s(
|
|
26
|
-
maxDelay: time.s(
|
|
23
|
+
delay: time.s(1.5),
|
|
24
|
+
factor: 1.5,
|
|
25
|
+
interval: time.s(15),
|
|
26
|
+
maxDelay: time.s(5),
|
|
27
27
|
timeoutError: phrase.loginExpired(),
|
|
28
28
|
};
|
|
29
29
|
|
|
@@ -48,7 +48,7 @@ const flow = function ({ id, secret: token }) {
|
|
|
48
48
|
const createFlow = function ({ expiresAt, secret, type }, onLoginCreated) {
|
|
49
49
|
return postFlow({ expiresAt, secret, type }).then(({ code, id }) => {
|
|
50
50
|
if (onLoginCreated) {
|
|
51
|
-
onLoginCreated({ code,
|
|
51
|
+
onLoginCreated({ code, secret, url: cfgurl.LINK });
|
|
52
52
|
}
|
|
53
53
|
return { id };
|
|
54
54
|
});
|
|
@@ -77,7 +77,7 @@ const pollFlow = function (value, onLoginPoll) {
|
|
|
77
77
|
// [function]
|
|
78
78
|
|
|
79
79
|
const main = function ({ onLoginCreated, onLoginPoll }) {
|
|
80
|
-
const expiresAt = new Date(Date.now() + time.m(
|
|
80
|
+
const expiresAt = new Date(Date.now() + time.m(10));
|
|
81
81
|
const secret = randomUUID();
|
|
82
82
|
return createFlow(
|
|
83
83
|
{
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// [export]
|
|
4
|
+
|
|
5
|
+
module.exports = [
|
|
6
|
+
"banner.png",
|
|
7
|
+
"banner_video.mp4",
|
|
8
|
+
"cover.png",
|
|
9
|
+
"cover_video.mp4",
|
|
10
|
+
"icon.png",
|
|
11
|
+
"icon_video.mp4",
|
|
12
|
+
"poster.png",
|
|
13
|
+
"poster_video.mp4",
|
|
14
|
+
"wordmark.svg",
|
|
15
|
+
"wordmark_animated.svg",
|
|
16
|
+
];
|
package/src/parser/media.js
CHANGED
|
@@ -10,7 +10,10 @@ const fs = require("fs-extra");
|
|
|
10
10
|
|
|
11
11
|
// [import]
|
|
12
12
|
|
|
13
|
+
const cfgart = require("../config/art");
|
|
14
|
+
const cfgimage = require("../config/image");
|
|
13
15
|
const cfgproduct = require("../config/product");
|
|
16
|
+
const cfgvideo = require("../config/video");
|
|
14
17
|
const objs = require("../util/objs");
|
|
15
18
|
|
|
16
19
|
// [use]
|
|
@@ -24,18 +27,7 @@ const readdir = fs.promises.readdir;
|
|
|
24
27
|
|
|
25
28
|
// [declare]
|
|
26
29
|
|
|
27
|
-
const ART =
|
|
28
|
-
"banner.png",
|
|
29
|
-
"banner_video.mp4",
|
|
30
|
-
"cover.png",
|
|
31
|
-
"cover_video.mp4",
|
|
32
|
-
"icon.png",
|
|
33
|
-
"icon_video.mp4",
|
|
34
|
-
"poster.png",
|
|
35
|
-
"poster_video.mp4",
|
|
36
|
-
"wordmark.svg",
|
|
37
|
-
"wordmark_animated.svg",
|
|
38
|
-
];
|
|
30
|
+
const ART = cfgart;
|
|
39
31
|
|
|
40
32
|
// [declare]
|
|
41
33
|
|
|
@@ -62,8 +54,9 @@ const art = function (root) {
|
|
|
62
54
|
// [function]
|
|
63
55
|
|
|
64
56
|
const handleGallery = function (entries) {
|
|
57
|
+
const exts = [...Object.keys(cfgimage), ...Object.keys(cfgvideo)];
|
|
65
58
|
return entries
|
|
66
|
-
.filter((f) => extname(f).toLowerCase()
|
|
59
|
+
.filter((f) => exts.includes(extname(f).toLowerCase()))
|
|
67
60
|
.map((f) => join(MEDIA, "gallery", f))
|
|
68
61
|
.sort();
|
|
69
62
|
};
|
package/src/schema/game.js
CHANGED
|
@@ -11,10 +11,12 @@ const z = require("zod");
|
|
|
11
11
|
const cfgaudience = require("../config/audience");
|
|
12
12
|
const cfggenre = require("../config/genre");
|
|
13
13
|
const cfghtml = require("../config/html");
|
|
14
|
+
const cfgimage = require("../config/image");
|
|
14
15
|
const cfginput = require("../config/input");
|
|
15
16
|
const cfgorientation = require("../config/orientation");
|
|
16
17
|
const cfgproduct = require("../config/product");
|
|
17
18
|
const cfgurl = require("../config/url");
|
|
19
|
+
const cfgvideo = require("../config/video");
|
|
18
20
|
const cfgvisibility = require("../config/visibility");
|
|
19
21
|
const check = require("./check/game");
|
|
20
22
|
const dom = require("../util/dom");
|
|
@@ -94,33 +96,48 @@ const zMedia = function (field, root) {
|
|
|
94
96
|
// [function]
|
|
95
97
|
|
|
96
98
|
const zPng = function (field, root, opts) {
|
|
97
|
-
return zMedia(field, root).
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
return zMedia(field, root).superRefine((file, ctx) => {
|
|
100
|
+
const issues = imageCheck(root, file, { ...opts, type: cfgimage[".png"] });
|
|
101
|
+
if (issues.length) {
|
|
102
|
+
ctx.addIssue({
|
|
103
|
+
code: "custom",
|
|
104
|
+
...message(field, ["Invalid PNG.", ...issues]),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
});
|
|
101
108
|
};
|
|
102
109
|
|
|
103
110
|
// [function]
|
|
104
111
|
|
|
105
112
|
const zSvg = function (field, root, opts) {
|
|
106
|
-
return zMedia(field, root).
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
return zMedia(field, root).superRefine((file, ctx) => {
|
|
114
|
+
const issues = imageCheck(root, file, { ...opts, type: cfgimage[".svg"] });
|
|
115
|
+
if (issues.length) {
|
|
116
|
+
ctx.addIssue({
|
|
117
|
+
code: "custom",
|
|
118
|
+
...message(field, ["Invalid SVG.", ...issues]),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
});
|
|
110
122
|
};
|
|
111
123
|
|
|
112
124
|
// [function]
|
|
113
125
|
|
|
114
126
|
const zMp4 = function (field, root, opts) {
|
|
115
|
-
return zMedia(field, root).
|
|
116
|
-
(file
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
return zMedia(field, root).superRefine((file, ctx) => {
|
|
128
|
+
const issues = videoCheck(root, file, {
|
|
129
|
+
maxDuration: 30,
|
|
130
|
+
minDuration: 3,
|
|
131
|
+
...opts,
|
|
132
|
+
type: cfgvideo[".mp4"],
|
|
133
|
+
});
|
|
134
|
+
if (issues.length) {
|
|
135
|
+
ctx.addIssue({
|
|
136
|
+
code: "custom",
|
|
137
|
+
...message(field, ["Invalid MP4.", ...issues]),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
});
|
|
124
141
|
};
|
|
125
142
|
|
|
126
143
|
// [function]
|
|
@@ -171,7 +188,7 @@ const zBannerVideo = function (root) {
|
|
|
171
188
|
return z.optional(
|
|
172
189
|
zMp4(field, root, {
|
|
173
190
|
aspect: "2:1",
|
|
174
|
-
maxSize: size.mb(
|
|
191
|
+
maxSize: size.mb(15),
|
|
175
192
|
minHeight: 512,
|
|
176
193
|
minWidth: 1024,
|
|
177
194
|
}),
|
|
@@ -206,7 +223,7 @@ const zCoverVideo = function (root) {
|
|
|
206
223
|
return z.optional(
|
|
207
224
|
zMp4(field, root, {
|
|
208
225
|
aspect: "3:2",
|
|
209
|
-
maxSize: size.mb(
|
|
226
|
+
maxSize: size.mb(15),
|
|
210
227
|
minHeight: 512,
|
|
211
228
|
minWidth: 768,
|
|
212
229
|
}),
|
|
@@ -241,10 +258,44 @@ const zGallery = function (root) {
|
|
|
241
258
|
return z.optional(
|
|
242
259
|
z
|
|
243
260
|
.array(
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
261
|
+
zMedia(field, root).superRefine((file, ctx) => {
|
|
262
|
+
if (file.endsWith(".png")) {
|
|
263
|
+
const issues = imageCheck(root, file, {
|
|
264
|
+
maxSize: size.mb(2),
|
|
265
|
+
type: cfgimage[".png"],
|
|
266
|
+
});
|
|
267
|
+
if (issues.length) {
|
|
268
|
+
ctx.addIssue({
|
|
269
|
+
code: "custom",
|
|
270
|
+
...message(field, [`Invalid PNG: ${file}`, ...issues]),
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
} else if (file.endsWith(".svg")) {
|
|
274
|
+
const issues = imageCheck(root, file, {
|
|
275
|
+
maxSize: size.mb(1),
|
|
276
|
+
type: cfgimage[".svg"],
|
|
277
|
+
});
|
|
278
|
+
if (issues.length) {
|
|
279
|
+
ctx.addIssue({
|
|
280
|
+
code: "custom",
|
|
281
|
+
...message(field, [`Invalid SVG: ${file}`, ...issues]),
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
} else if (file.endsWith(".mp4")) {
|
|
285
|
+
const issues = videoCheck(root, file, {
|
|
286
|
+
maxDuration: 30,
|
|
287
|
+
maxSize: size.mb(15),
|
|
288
|
+
minDuration: 3,
|
|
289
|
+
type: cfgvideo[".mp4"],
|
|
290
|
+
});
|
|
291
|
+
if (issues.length) {
|
|
292
|
+
ctx.addIssue({
|
|
293
|
+
code: "custom",
|
|
294
|
+
...message(field, [`Invalid MP4: ${file}`, ...issues]),
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}),
|
|
248
299
|
)
|
|
249
300
|
.min(1)
|
|
250
301
|
.max(12),
|
|
@@ -293,7 +344,7 @@ const zIconVideo = function (root) {
|
|
|
293
344
|
return z.optional(
|
|
294
345
|
zMp4(field, root, {
|
|
295
346
|
aspect: "1:1",
|
|
296
|
-
maxSize: size.mb(
|
|
347
|
+
maxSize: size.mb(15),
|
|
297
348
|
minHeight: 512,
|
|
298
349
|
minWidth: 512,
|
|
299
350
|
}),
|
|
@@ -353,7 +404,7 @@ const zPosterVideo = function (root) {
|
|
|
353
404
|
return z.optional(
|
|
354
405
|
zMp4(field, root, {
|
|
355
406
|
aspect: "2:3",
|
|
356
|
-
maxSize: size.mb(
|
|
407
|
+
maxSize: size.mb(15),
|
|
357
408
|
minHeight: 768,
|
|
358
409
|
minWidth: 512,
|
|
359
410
|
}),
|
|
@@ -437,7 +488,6 @@ const zWordmark = function (root) {
|
|
|
437
488
|
const field = "wordmark";
|
|
438
489
|
return z.optional(
|
|
439
490
|
zSvg(field, root, {
|
|
440
|
-
aspect: "1:1",
|
|
441
491
|
maxSize: size.kb(512),
|
|
442
492
|
}),
|
|
443
493
|
);
|
|
@@ -449,7 +499,6 @@ const zWordmarkAnimated = function (root) {
|
|
|
449
499
|
const field = "wordmark_animated";
|
|
450
500
|
return z.optional(
|
|
451
501
|
zSvg(field, root, {
|
|
452
|
-
aspect: "1:1",
|
|
453
502
|
maxSize: size.mb(1),
|
|
454
503
|
}),
|
|
455
504
|
);
|
package/src/schema/workflow.js
CHANGED
|
@@ -29,7 +29,7 @@ const MEDIA = `.${HANDLE}`;
|
|
|
29
29
|
// [function]
|
|
30
30
|
|
|
31
31
|
const reference = function (field) {
|
|
32
|
-
return chalk.cyan.underline(`${cfgurl.MANIFEST}#
|
|
32
|
+
return chalk.cyan.underline(`${cfgurl.MANIFEST}#game.${field}`);
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
// [function]
|
|
@@ -64,11 +64,11 @@ const zDeployDir = function (root, { build }) {
|
|
|
64
64
|
)
|
|
65
65
|
.refine(
|
|
66
66
|
(file) => isOutCheck(root, file),
|
|
67
|
-
message(field, ["Must be within the project
|
|
67
|
+
message(field, ["Must be within the project."]),
|
|
68
68
|
)
|
|
69
69
|
.refine(
|
|
70
70
|
(file) => isReservedCheck(root, file, MEDIA),
|
|
71
|
-
message(field, [`Must not contain ${MEDIA}.`]),
|
|
71
|
+
message(field, [`Must not contain ${MEDIA} in path.`]),
|
|
72
72
|
)
|
|
73
73
|
.refine(
|
|
74
74
|
(file) => build || existsCheck(root, file),
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// [function]
|
|
4
|
+
|
|
5
|
+
const formatBytes = function (bytes) {
|
|
6
|
+
switch (true) {
|
|
7
|
+
case bytes < 1024:
|
|
8
|
+
return `${bytes} B`;
|
|
9
|
+
case bytes < 1024 ** 2:
|
|
10
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
11
|
+
case bytes < 1024 ** 3:
|
|
12
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
13
|
+
default:
|
|
14
|
+
return `${(bytes / 1024 / 1024 / 1024).toFixed(1)} GB`;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// [export]
|
|
19
|
+
|
|
20
|
+
module.exports = { formatBytes };
|
package/src/util/image.js
CHANGED
|
@@ -2,11 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
// [import]
|
|
4
4
|
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
|
|
7
|
+
// [import]
|
|
8
|
+
|
|
5
9
|
const fs = require("fs-extra");
|
|
6
10
|
const imageSize = require("image-size").imageSize;
|
|
7
11
|
|
|
12
|
+
// [import]
|
|
13
|
+
|
|
14
|
+
const bytes = require("./bytes");
|
|
15
|
+
const cfgimage = require("../config/image");
|
|
16
|
+
|
|
8
17
|
// [use]
|
|
9
18
|
|
|
19
|
+
const extname = path.extname;
|
|
20
|
+
const formatBytes = bytes.formatBytes;
|
|
10
21
|
const readFileSync = fs.readFileSync; // eslint-disable-line no-sync
|
|
11
22
|
const statSync = fs.statSync; // eslint-disable-line no-sync
|
|
12
23
|
|
|
@@ -41,42 +52,42 @@ const checkSize = function (file, maxSize) {
|
|
|
41
52
|
|
|
42
53
|
const checkPng = function (file, { aspect, maxSize, minHeight, minWidth }) {
|
|
43
54
|
if (!checkSize(file, maxSize)) {
|
|
44
|
-
return
|
|
55
|
+
return [`Image size must not exceed: ${formatBytes(maxSize)}`];
|
|
45
56
|
}
|
|
46
57
|
const buffer = readFileSync(file);
|
|
47
58
|
const { height, width } = imageSize(buffer);
|
|
48
59
|
if (minWidth && width < minWidth) {
|
|
49
|
-
return
|
|
60
|
+
return [`Image width must be at least: ${minWidth}px`];
|
|
50
61
|
}
|
|
51
62
|
if (minHeight && height < minHeight) {
|
|
52
|
-
return
|
|
63
|
+
return [`Image height must be at least: ${minHeight}px`];
|
|
53
64
|
}
|
|
54
65
|
if (!checkAspect(width, height, aspect)) {
|
|
55
|
-
return
|
|
66
|
+
return [`Image aspect ratio must be: ${aspect}`];
|
|
56
67
|
}
|
|
57
|
-
return
|
|
68
|
+
return [];
|
|
58
69
|
};
|
|
59
70
|
|
|
60
71
|
// [function]
|
|
61
72
|
|
|
62
73
|
const checkSvg = function (file, { maxSize }) {
|
|
63
74
|
if (!checkSize(file, maxSize)) {
|
|
64
|
-
return
|
|
75
|
+
return [`Image size must not exceed: ${formatBytes(maxSize)}`];
|
|
65
76
|
}
|
|
66
|
-
return
|
|
77
|
+
return [];
|
|
67
78
|
};
|
|
68
79
|
|
|
69
80
|
// [function]
|
|
70
81
|
|
|
71
82
|
const check = function (file, { type, ...opts }) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
83
|
+
const checks = {
|
|
84
|
+
[cfgimage[".png"]]: checkPng,
|
|
85
|
+
[cfgimage[".svg"]]: checkSvg,
|
|
86
|
+
};
|
|
87
|
+
const checkImage = checks[type];
|
|
88
|
+
return checkImage && cfgimage[extname(file)] === type
|
|
89
|
+
? checkImage(file, opts)
|
|
90
|
+
: ["Image has an unsupported file type."];
|
|
80
91
|
};
|
|
81
92
|
|
|
82
93
|
// [export]
|
package/src/util/progress.js
CHANGED
|
@@ -4,8 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
const cliProgress = require("cli-progress");
|
|
6
6
|
|
|
7
|
+
// [import]
|
|
8
|
+
|
|
9
|
+
const bytes = require("./bytes");
|
|
10
|
+
|
|
7
11
|
// [use]
|
|
8
12
|
|
|
13
|
+
const formatBytes = bytes.formatBytes;
|
|
9
14
|
const Presets = cliProgress.Presets;
|
|
10
15
|
const SingleBar = cliProgress.SingleBar;
|
|
11
16
|
|
|
@@ -15,21 +20,6 @@ const FORMAT = "[{bar}] {percentage}% | {value}/{total}";
|
|
|
15
20
|
|
|
16
21
|
// [function]
|
|
17
22
|
|
|
18
|
-
const formatBytes = function (bytes) {
|
|
19
|
-
switch (true) {
|
|
20
|
-
case bytes < 1000:
|
|
21
|
-
return `${bytes} B`;
|
|
22
|
-
case bytes < 1000 * 1000:
|
|
23
|
-
return `${(bytes / 1000).toFixed(1)} KB`;
|
|
24
|
-
case bytes < 1000 * 1000 * 1000:
|
|
25
|
-
return `${(bytes / 1000 / 1000).toFixed(1)} MB`;
|
|
26
|
-
default:
|
|
27
|
-
return `${(bytes / 1000 / 1000 / 1000).toFixed(1)} GB`;
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// [function]
|
|
32
|
-
|
|
33
23
|
const formatValue = function (value, opts, type) {
|
|
34
24
|
if (type === "percentage") {
|
|
35
25
|
return value;
|
package/src/util/schema.js
CHANGED
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
// [import]
|
|
4
|
+
|
|
5
|
+
const chalk = require("chalk");
|
|
6
|
+
|
|
7
|
+
// [function]
|
|
8
|
+
|
|
9
|
+
const dereference = function (issues) {
|
|
10
|
+
const references = new Set();
|
|
11
|
+
const output = issues.map((issue) =>
|
|
12
|
+
issue
|
|
13
|
+
.split("\n")
|
|
14
|
+
.filter((line) => {
|
|
15
|
+
if (/https?:\/\//u.test(line)) {
|
|
16
|
+
references.add(line.trim());
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
})
|
|
21
|
+
.join("\n"),
|
|
22
|
+
);
|
|
23
|
+
return [output, [...references]];
|
|
24
|
+
};
|
|
25
|
+
|
|
3
26
|
// [function]
|
|
4
27
|
|
|
5
28
|
const format = function (name, { issues }) {
|
|
6
29
|
return issues.map(
|
|
7
|
-
({ message, path }) =>
|
|
30
|
+
({ message, path }) =>
|
|
31
|
+
`${chalk.cyan(`${name}.${path.join(".") || "value"}`)}: ${message}`,
|
|
8
32
|
);
|
|
9
33
|
};
|
|
10
34
|
|
|
@@ -22,4 +46,4 @@ const validator = function (name, schema) {
|
|
|
22
46
|
|
|
23
47
|
// [export]
|
|
24
48
|
|
|
25
|
-
module.exports = { validator };
|
|
49
|
+
module.exports = { dereference, validator };
|
package/src/util/video.js
CHANGED
|
@@ -2,15 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
// [import]
|
|
4
4
|
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
|
|
7
|
+
// [import]
|
|
8
|
+
|
|
5
9
|
const createFile = require("mp4box").createFile;
|
|
6
10
|
const fs = require("fs-extra");
|
|
7
11
|
|
|
8
12
|
// [import]
|
|
9
13
|
|
|
14
|
+
const bytes = require("./bytes");
|
|
15
|
+
const cfgvideo = require("../config/video");
|
|
10
16
|
const phrase = require("../phrase");
|
|
11
17
|
|
|
12
18
|
// [use]
|
|
13
19
|
|
|
20
|
+
const extname = path.extname;
|
|
21
|
+
const formatBytes = bytes.formatBytes;
|
|
14
22
|
const readFileSync = fs.readFileSync; // eslint-disable-line no-sync
|
|
15
23
|
const statSync = fs.statSync; // eslint-disable-line no-sync
|
|
16
24
|
|
|
@@ -83,42 +91,46 @@ const readMp4 = function (file) {
|
|
|
83
91
|
|
|
84
92
|
const checkMp4 = function (
|
|
85
93
|
file,
|
|
86
|
-
{ aspect, maxDuration, maxSize, minHeight, minWidth },
|
|
94
|
+
{ aspect, maxDuration, maxSize, minDuration, minHeight, minWidth },
|
|
87
95
|
) {
|
|
88
96
|
if (!checkSize(file, maxSize)) {
|
|
89
|
-
return
|
|
97
|
+
return [`Video size must not exceed: ${formatBytes(maxSize)}`];
|
|
90
98
|
}
|
|
91
99
|
let video = null;
|
|
92
100
|
try {
|
|
93
101
|
video = readMp4(file);
|
|
94
102
|
} catch {
|
|
95
|
-
return
|
|
103
|
+
return ["Video is an invalid file."];
|
|
96
104
|
}
|
|
97
105
|
const { duration, height, width } = video;
|
|
98
106
|
if (minWidth && width < minWidth) {
|
|
99
|
-
return
|
|
107
|
+
return [`Video width must be at least: ${minWidth}px`];
|
|
100
108
|
}
|
|
101
109
|
if (minHeight && height < minHeight) {
|
|
102
|
-
return
|
|
110
|
+
return [`Video height must be at least: ${minHeight}px`];
|
|
111
|
+
}
|
|
112
|
+
if (minDuration && duration < minDuration) {
|
|
113
|
+
return [`Video duration must be at least: ${minDuration}s`];
|
|
103
114
|
}
|
|
104
115
|
if (maxDuration && duration > maxDuration) {
|
|
105
|
-
return
|
|
116
|
+
return [`Video duration must not exceed: ${maxDuration}s`];
|
|
106
117
|
}
|
|
107
118
|
if (!checkAspect(width, height, aspect)) {
|
|
108
|
-
return
|
|
119
|
+
return [`Video aspect ratio must be: ${aspect}`];
|
|
109
120
|
}
|
|
110
|
-
return
|
|
121
|
+
return [];
|
|
111
122
|
};
|
|
112
123
|
|
|
113
124
|
// [function]
|
|
114
125
|
|
|
115
126
|
const check = function (file, { type, ...opts }) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
127
|
+
const checks = {
|
|
128
|
+
[cfgvideo[".mp4"]]: checkMp4,
|
|
129
|
+
};
|
|
130
|
+
const checkVideo = checks[type];
|
|
131
|
+
return checkVideo && cfgvideo[extname(file)] === type
|
|
132
|
+
? checkVideo(file, opts)
|
|
133
|
+
: ["Video has an unsupported file type."];
|
|
122
134
|
};
|
|
123
135
|
|
|
124
136
|
// [export]
|