@vnejs/tools 0.0.28
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 +24 -0
- package/src/index.js +30 -0
- package/src/lockeys/index.js +54 -0
- package/src/resize/audio.js +74 -0
- package/src/resize/image.js +86 -0
- package/src/resize/index.js +8 -0
- package/src/resize/utils.js +18 -0
- package/src/resize/video.js +83 -0
- package/src/utils/filesystem.js +29 -0
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vnejs/tools",
|
|
3
|
+
"version": "0.0.28",
|
|
4
|
+
"description": "CLI tools of @vnejs",
|
|
5
|
+
"source": "src/index.js",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"@vnejs/tools": "./src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
12
|
+
"publish:major": "npm version major && npm publish --access public",
|
|
13
|
+
"publish:minor": "npm version minor && npm publish --access public",
|
|
14
|
+
"publish:patch": "npm version patch && npm publish --access public"
|
|
15
|
+
},
|
|
16
|
+
"author": "",
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@ffmpeg-installer/ffmpeg": "1.1.0",
|
|
20
|
+
"commander": "13.1.0",
|
|
21
|
+
"fluent-ffmpeg": "2.1.3",
|
|
22
|
+
"sharp": "0.33.5"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require("commander");
|
|
4
|
+
|
|
5
|
+
const resizeAction = require("./resize");
|
|
6
|
+
const locKeysAction = require("./lockeys");
|
|
7
|
+
|
|
8
|
+
const packageJson = require("../package.json");
|
|
9
|
+
|
|
10
|
+
const program = new Command();
|
|
11
|
+
|
|
12
|
+
program.name(packageJson.name).description(packageJson.description).version(packageJson.version);
|
|
13
|
+
|
|
14
|
+
program
|
|
15
|
+
.command("resize")
|
|
16
|
+
.option("-t, --target <target>", "resize target", "all")
|
|
17
|
+
.option("-w, --web", "web mode (less quality)", false)
|
|
18
|
+
.option("-q, --quiet", "no console mode", false)
|
|
19
|
+
.option("--type <type>", "resize type (image, video, audio)")
|
|
20
|
+
.option("-m, --media <media...>", "media directories")
|
|
21
|
+
.action(resizeAction);
|
|
22
|
+
|
|
23
|
+
program
|
|
24
|
+
.command("lockeys")
|
|
25
|
+
.option("-m, --mode <mode>", "dirname inside dir 'game'")
|
|
26
|
+
.option("-s, --scenario <scenario>", "scenario file inside mod dir")
|
|
27
|
+
.option("-l, --langs <langs...>", "langs to create locfiles")
|
|
28
|
+
.action(locKeysAction);
|
|
29
|
+
|
|
30
|
+
program.parse();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
|
|
4
|
+
const { readFile } = require("../utils/filesystem");
|
|
5
|
+
|
|
6
|
+
const gameDir = path.join(process.env.PWD, "game");
|
|
7
|
+
|
|
8
|
+
const quotesRegepx = /\"(.*?)\"/g;
|
|
9
|
+
|
|
10
|
+
module.exports = async (options) => {
|
|
11
|
+
if (!options.scenario || !options.langs || !options.mode) {
|
|
12
|
+
console.error(`No values for scenario file or langs or mode`);
|
|
13
|
+
console.error(`sceanrio: ${options.scenario}`);
|
|
14
|
+
console.error(`langs: ${options.langs}`);
|
|
15
|
+
console.error(`mode: ${options.mode}`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const lines = await readFile(path.join(gameDir, options.mode, "scenario", options.scenario));
|
|
20
|
+
const result = {};
|
|
21
|
+
|
|
22
|
+
let curLabel = "";
|
|
23
|
+
|
|
24
|
+
lines
|
|
25
|
+
.toString()
|
|
26
|
+
.split("\n")
|
|
27
|
+
.filter((line) => line.startsWith("label") || line.includes('"'))
|
|
28
|
+
.forEach((line) => {
|
|
29
|
+
if (line.startsWith("label")) {
|
|
30
|
+
curLabel = line.slice(6, line.length - (line.endsWith(":") ? 1 : 0));
|
|
31
|
+
result[curLabel] = {};
|
|
32
|
+
} else {
|
|
33
|
+
line.match(quotesRegepx).forEach((text) => {
|
|
34
|
+
const valueWithoutQuotes = text.slice(1, text.length - 1);
|
|
35
|
+
result[curLabel][valueWithoutQuotes] = valueWithoutQuotes;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
Object.keys(result).forEach((labelName) => {
|
|
41
|
+
options.langs.forEach((lang) => {
|
|
42
|
+
const newContent = { ...result[labelName] };
|
|
43
|
+
const filePath = path.join(gameDir, options.mode, "media", "locs", lang, `${labelName}.json`);
|
|
44
|
+
if (fs.existsSync(filePath)) {
|
|
45
|
+
const oldContent = JSON.parse(fs.readFileSync(filePath));
|
|
46
|
+
|
|
47
|
+
Object.keys(oldContent).forEach((key) => {
|
|
48
|
+
if (newContent[key] && oldContent[key] !== key) newContent[key] = oldContent[key];
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
fs.writeFileSync(filePath, JSON.stringify(newContent, null, 2));
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
|
|
3
|
+
const { createDir, getFilesInDir, readDir } = require("../utils/filesystem");
|
|
4
|
+
const { clear, ffmpeg } = require("./utils");
|
|
5
|
+
|
|
6
|
+
const gameDir = path.join(process.env.PWD, "game");
|
|
7
|
+
|
|
8
|
+
const resizeOne = async (toDirName, mediaDir, mediaTypes, quality, fromDirName, options) => {
|
|
9
|
+
const promises = [];
|
|
10
|
+
|
|
11
|
+
await Promise.all(
|
|
12
|
+
mediaTypes.map(async (mediaType) => {
|
|
13
|
+
const files = await getFilesInDir(path.join(mediaDir, mediaType, fromDirName));
|
|
14
|
+
|
|
15
|
+
if (files.length) await createDir(path.join(mediaDir, mediaType, toDirName));
|
|
16
|
+
|
|
17
|
+
files.forEach((filename) => {
|
|
18
|
+
if (filename.startsWith(".")) return;
|
|
19
|
+
|
|
20
|
+
promises.push(async () => {
|
|
21
|
+
const fromFullName = path.join(mediaDir, mediaType, fromDirName, filename);
|
|
22
|
+
const splittedFileName = filename.split("/");
|
|
23
|
+
const toFullName = path.join(mediaDir, mediaType, toDirName, filename);
|
|
24
|
+
const toFullNameMp3 = toFullName.slice(0, toFullName.lastIndexOf(".")) + ".mp3";
|
|
25
|
+
|
|
26
|
+
await createDir(
|
|
27
|
+
path.join(mediaDir, mediaType, toDirName, ...splittedFileName.slice(0, splittedFileName.length - 1))
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
31
|
+
|
|
32
|
+
return new Promise((resolve) =>
|
|
33
|
+
ffmpeg(fromFullName)
|
|
34
|
+
.inputOptions(["-re"])
|
|
35
|
+
.audioBitrate(quality)
|
|
36
|
+
.audioCodec("libmp3lame")
|
|
37
|
+
.on("start", () => !options.quiet && console.log("start resizing ", filename, quality))
|
|
38
|
+
.on("end", () => {
|
|
39
|
+
!options.quiet && console.log("finish resizing ", filename, quality);
|
|
40
|
+
resolve();
|
|
41
|
+
})
|
|
42
|
+
.save(toFullNameMp3)
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
})
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
await Promise.all(promises.map((i) => i()));
|
|
50
|
+
};
|
|
51
|
+
const resize = async (toDirName, mediaTypes, quality, fromDirName, options) => {
|
|
52
|
+
const mods = await readDir(gameDir);
|
|
53
|
+
|
|
54
|
+
return Promise.all(
|
|
55
|
+
mods.map((mod) => resizeOne(toDirName, path.join(gameDir, mod, "media"), mediaTypes, quality, fromDirName, options))
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
module.exports = async (options) => {
|
|
60
|
+
const promises = [];
|
|
61
|
+
const addPromise = (c, quality) =>
|
|
62
|
+
promises.push(async () => {
|
|
63
|
+
if (!c) return;
|
|
64
|
+
|
|
65
|
+
const dirName = `${quality}k`;
|
|
66
|
+
await clear(dirName, options.media);
|
|
67
|
+
await resize(dirName, options.media, quality, "original", options);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
addPromise(["96", "all", "dev"].includes(options.target), 96);
|
|
71
|
+
addPromise(["128", "all"].includes(options.target), 128);
|
|
72
|
+
|
|
73
|
+
for (let i = 0; i < promises.length; i++) await promises[i]();
|
|
74
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const sharp = require("sharp");
|
|
3
|
+
|
|
4
|
+
const { createDir, getFilesInDir, readFile, readDir } = require("../utils/filesystem");
|
|
5
|
+
const { clear } = require("./utils");
|
|
6
|
+
|
|
7
|
+
const gameDir = path.join(process.env.PWD, "game");
|
|
8
|
+
|
|
9
|
+
const resizeOne = async (toDirName, mediaDir, mediaTypes, getSizes, fromDirName, options) => {
|
|
10
|
+
const promises = [];
|
|
11
|
+
const quality = options.web ? 80 : 100;
|
|
12
|
+
|
|
13
|
+
await Promise.all(
|
|
14
|
+
mediaTypes.map(async (mediaType) => {
|
|
15
|
+
const files = await getFilesInDir(path.join(mediaDir, mediaType, fromDirName));
|
|
16
|
+
|
|
17
|
+
if (files.length) await createDir(path.join(mediaDir, mediaType, toDirName));
|
|
18
|
+
|
|
19
|
+
files.forEach((filename) => {
|
|
20
|
+
if (filename.startsWith(".")) return;
|
|
21
|
+
|
|
22
|
+
promises.push(async () => {
|
|
23
|
+
const img = await readFile(path.join(mediaDir, mediaType, fromDirName, filename));
|
|
24
|
+
const splittedFileName = filename.split("/");
|
|
25
|
+
|
|
26
|
+
await createDir(
|
|
27
|
+
path.join(mediaDir, mediaType, toDirName, ...splittedFileName.slice(0, splittedFileName.length - 1))
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const isGif = filename.endsWith(".gif");
|
|
34
|
+
const image = sharp(img, { animated: isGif });
|
|
35
|
+
const resultFilename = path.join(mediaDir, mediaType, toDirName, filename);
|
|
36
|
+
|
|
37
|
+
await image
|
|
38
|
+
.clone()
|
|
39
|
+
.resize(...getSizes(await image.metadata()))
|
|
40
|
+
[isGif ? "gif" : "webp"]({ quality, alphaQuality: 100 })
|
|
41
|
+
.toFile(isGif ? resultFilename : `${resultFilename.slice(0, resultFilename.lastIndexOf("."))}.webp`);
|
|
42
|
+
} catch (e) {
|
|
43
|
+
!options.quiet && console.log("resized error", toDirName, mediaType, filename, e);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
!options.quiet && console.log("resized success", toDirName, mediaType, filename);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
await Promise.all(promises.map((promiseFunc) => promiseFunc()));
|
|
54
|
+
};
|
|
55
|
+
const resize = async (toDirName, mediaTypes, getSizes, fromDirName, options) => {
|
|
56
|
+
const mods = await readDir(gameDir);
|
|
57
|
+
|
|
58
|
+
return Promise.all(
|
|
59
|
+
mods.map((mod) =>
|
|
60
|
+
resizeOne(toDirName, path.join(gameDir, mod, "media"), mediaTypes, getSizes, fromDirName, options)
|
|
61
|
+
)
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
module.exports = async (options) => {
|
|
66
|
+
const promises = [];
|
|
67
|
+
const addPromise = (c, height) =>
|
|
68
|
+
promises.push(async () => {
|
|
69
|
+
if (!c) return;
|
|
70
|
+
const dirName = `${height}p`;
|
|
71
|
+
await clear(dirName, options.media);
|
|
72
|
+
await Promise.all([
|
|
73
|
+
resize(dirName, options.media, (d) => [Math.round((d.width * height) / 1080)], "original", options),
|
|
74
|
+
resize(dirName, options.media, (d) => [Math.round((d.width * height) / 2160)], "original-4k", options),
|
|
75
|
+
]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
addPromise(["all", "mobile", "360"].includes(options.target), 360);
|
|
79
|
+
addPromise(["all", "mobile", "540"].includes(options.target), 540);
|
|
80
|
+
addPromise(["all", "mobile", "desktop", "test", "720"].includes(options.target), 720);
|
|
81
|
+
addPromise(["all", "mobile", "desktop", "test", "dev", "1080"].includes(options.target), 1080);
|
|
82
|
+
addPromise(["all", "desktop", "1440"].includes(options.target), 1440);
|
|
83
|
+
addPromise(["all", "desktop", "2160"].includes(options.target), 2160);
|
|
84
|
+
|
|
85
|
+
for (let i = 0; i < promises.length; i++) await promises[i]();
|
|
86
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
module.exports = (options) => {
|
|
2
|
+
if (!["image", "video", "audio"].includes(options.type))
|
|
3
|
+
return console.error("error: incorrect or missing 'type' option");
|
|
4
|
+
|
|
5
|
+
if (options.type === "audio") return require("./audio")(options);
|
|
6
|
+
if (options.type === "image") return require("./image")(options);
|
|
7
|
+
if (options.type === "video") return require("./video")(options);
|
|
8
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const ffmpegPath = require("@ffmpeg-installer/ffmpeg").path;
|
|
3
|
+
const ffmpeg = require("fluent-ffmpeg");
|
|
4
|
+
ffmpeg.setFfmpegPath(ffmpegPath);
|
|
5
|
+
|
|
6
|
+
const { removeDir, readDir } = require("../utils/filesystem");
|
|
7
|
+
|
|
8
|
+
const gameDir = path.join(process.env.PWD, "game");
|
|
9
|
+
|
|
10
|
+
const clearOne = (dirName, mediaDir, mediaTypes) =>
|
|
11
|
+
Promise.all(mediaTypes.map((mediaType) => removeDir(path.join(mediaDir, mediaType, dirName))));
|
|
12
|
+
const clear = async (dirName, mediaTypes) => {
|
|
13
|
+
const mods = await readDir(gameDir);
|
|
14
|
+
|
|
15
|
+
return Promise.all(mods.map((mod) => clearOne(dirName, path.join(gameDir, mod, "media"), mediaTypes)));
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
module.exports = { clearOne, clear, ffmpeg };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
|
|
3
|
+
const { createDir, getFilesInDir, readDir } = require("../utils/filesystem");
|
|
4
|
+
const { clear, ffmpeg } = require("./utils");
|
|
5
|
+
|
|
6
|
+
const gameDir = path.join(process.env.PWD, "game");
|
|
7
|
+
|
|
8
|
+
const resizeOne = async (toDirName, mediaDir, mediaTypes, height, fromDirName, options) => {
|
|
9
|
+
const promises = [];
|
|
10
|
+
|
|
11
|
+
await Promise.all(
|
|
12
|
+
mediaTypes.map(async (mediaType) => {
|
|
13
|
+
const files = await getFilesInDir(path.join(mediaDir, mediaType, fromDirName));
|
|
14
|
+
|
|
15
|
+
if (files.length) await createDir(path.join(mediaDir, mediaType, toDirName));
|
|
16
|
+
|
|
17
|
+
files.forEach((filename) => {
|
|
18
|
+
if (filename.startsWith(".")) return;
|
|
19
|
+
|
|
20
|
+
promises.push(async () => {
|
|
21
|
+
const fromFullName = path.join(mediaDir, mediaType, fromDirName, filename);
|
|
22
|
+
const splittedFileName = filename.split("/");
|
|
23
|
+
const toFullName = path.join(mediaDir, mediaType, toDirName, filename);
|
|
24
|
+
const toFullNameMp4 = toFullName.slice(0, toFullName.lastIndexOf(".")) + ".mp4";
|
|
25
|
+
|
|
26
|
+
await createDir(
|
|
27
|
+
path.join(mediaDir, mediaType, toDirName, ...splittedFileName.slice(0, splittedFileName.length - 1))
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
31
|
+
|
|
32
|
+
return new Promise((resolve) =>
|
|
33
|
+
ffmpeg(fromFullName)
|
|
34
|
+
.inputOptions(["-re"])
|
|
35
|
+
.size(`?x${height}`)
|
|
36
|
+
.aspect("16:9")
|
|
37
|
+
.on("start", () => !options.quiet && console.log("start resizing ", filename, height))
|
|
38
|
+
.on("end", () => {
|
|
39
|
+
!options.quiet && console.log("finish resizing ", filename, height);
|
|
40
|
+
resolve();
|
|
41
|
+
})
|
|
42
|
+
.save(toFullNameMp4)
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
})
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
await Promise.all(promises.map((i) => i()));
|
|
50
|
+
};
|
|
51
|
+
const resize = async (toDirName, mediaTypes, height, fromDirName, options) => {
|
|
52
|
+
const mods = await readDir(gameDir);
|
|
53
|
+
|
|
54
|
+
return Promise.all(
|
|
55
|
+
mods.map((mod) => resizeOne(toDirName, path.join(gameDir, mod, "media"), mediaTypes, height, fromDirName, options))
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
module.exports = async (options) => {
|
|
60
|
+
const promises = [];
|
|
61
|
+
const addPromise = (c, height) =>
|
|
62
|
+
promises.push(async () => {
|
|
63
|
+
if (!c) return;
|
|
64
|
+
|
|
65
|
+
const dirName = `${height}p`;
|
|
66
|
+
await clear(dirName, options.media);
|
|
67
|
+
await Promise.all([
|
|
68
|
+
resize(dirName, options.media, height, "original", options),
|
|
69
|
+
resize(dirName, options.media, height, "original-4k", options),
|
|
70
|
+
]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
console.log(9999);
|
|
74
|
+
|
|
75
|
+
addPromise(["all", "mobile", "360"].includes(options.target), 360);
|
|
76
|
+
addPromise(["all", "mobile", "540"].includes(options.target), 540);
|
|
77
|
+
addPromise(["all", "mobile", "desktop", "test", "720"].includes(options.target), 720);
|
|
78
|
+
addPromise(["all", "mobile", "desktop", "test", "dev", "1080"].includes(options.target), 1080);
|
|
79
|
+
addPromise(["all", "desktop", "1440"].includes(options.target), 1440);
|
|
80
|
+
addPromise(["all", "desktop", "2160"].includes(options.target), 2160);
|
|
81
|
+
|
|
82
|
+
for (let i = 0; i < promises.length; i++) await promises[i]();
|
|
83
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
const createDir = (dirname) => new Promise((resolve) => fs.mkdir(dirname, { recursive: true }, resolve));
|
|
5
|
+
const removeDir = (pathname) => new Promise((resolve) => fs.rm(pathname, { recursive: true }, resolve));
|
|
6
|
+
const readFile = async (pathname) => new Promise((resolve) => fs.readFile(pathname, (err, data) => resolve(data)));
|
|
7
|
+
const readDir = async (pathname) => new Promise((resolve) => fs.readdir(pathname, (err, files) => resolve(files)));
|
|
8
|
+
const getLstat = async (pathname) => new Promise((resolve) => fs.lstat(pathname, (err, stats) => resolve(stats)));
|
|
9
|
+
|
|
10
|
+
const getFilesInDir = async (dirPath, prefix = "") => {
|
|
11
|
+
let result = await readDir(dirPath);
|
|
12
|
+
if (!result) return [];
|
|
13
|
+
let resultInfo = await Promise.all(result.map((fileOrDirPath) => getLstat(path.join(dirPath, fileOrDirPath))));
|
|
14
|
+
|
|
15
|
+
while (!resultInfo.every((stat) => stat.isFile())) {
|
|
16
|
+
const dirs = result.filter((_, i) => resultInfo[i].isDirectory());
|
|
17
|
+
result = result.filter((i) => !dirs.includes(i));
|
|
18
|
+
|
|
19
|
+
for (let i = 0; i < dirs.length; i++) {
|
|
20
|
+
result.push(...(await getFilesInDir(path.join(dirPath, dirs[i]), dirs[i])));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
resultInfo = await Promise.all(result.map((fileOrDirPath) => getLstat(path.join(dirPath, fileOrDirPath))));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return prefix ? result.map((filename) => `${prefix}/${filename}`) : result;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
module.exports = { createDir, removeDir, readFile, readDir, getLstat, getFilesInDir };
|