-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathpostbuild.js
More file actions
162 lines (131 loc) · 5.14 KB
/
Copy pathpostbuild.js
File metadata and controls
162 lines (131 loc) · 5.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import fs from 'fs';
import {ZipArchive} from "archiver";
import path, {resolve} from "path";
import jsonfile from 'jsonfile';
import gameName from "./currentGameName.js";
import {fileURLToPath} from "url";
import sharp from 'sharp';
import {execFile} from 'child_process'
import ffmpegStatic from 'ffmpeg-static'
const {readFileSync} = jsonfile;
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const gamePath = resolve(__dirname, `./src/Games/${gameName}`);
const resourcePath = resolve(__dirname, `./dist/resources`);
const tempResourcePath = path.join(resourcePath, '_tmp');
const gameInfoPath = `${gamePath}/gameinfo.json`;
const gameInfo = readFileSync(gameInfoPath);
// 1. Copy the gameinfo.json file.
fs.copyFile(gameInfoPath, 'dist/gameinfo.json', () => {
});
// 2. Include the game sources if specified to do so.
if (gameInfo.sourcesIncluded) {
fs.cpSync(gamePath, 'dist/sources', {recursive: true});
if (fs.existsSync('dist/sources/resources')) {
fs.rmSync('dist/sources/resources', {recursive: true});
}
await zipDirectory('dist/sources', 'dist/sources.zip');
fs.rmSync('dist/sources', {recursive: true});
}
// 3. Optimize jpg and png images using sharp.
const imageFiles = getFiles(resourcePath, ['.jpg', '.png']);
if (imageFiles?.length) {
await optimizeFiles('images', imageFiles, async (filePath, tempPath) => {
const sharpStream = sharp(filePath);
if (filePath.indexOf('.png') > -1) {
await sharpStream.png({quality: 80}).toFile(tempPath);
} else if (filePath.indexOf('.jpg') > -1) {
await sharpStream.jpeg({quality: 80}).toFile(tempPath);
}
});
}
// 4. Optimize sound files
const soundFiles = getFiles(resourcePath, ['.mp3']);
if (soundFiles?.length) {
if (!ffmpegStatic) {
console.error('FFmpeg is not installed. Please install it to optimize sound files.');
} else {
await optimizeFiles('sounds', soundFiles, async (filePath, tempPath) => {
execFile(ffmpegStatic, ['-i', filePath, '-b:a', '64k', tempPath], (error) => {
if (error) {
console.error(`Error converting ${filePath}:`, error.message);
reject(error);
} else {
resolve(filePath);
}
});
});
}
}
async function optimizeFiles(type, files, conversionFunc) {
console.log(`Optimize ${type}`)
await Promise.all(files.map(async (file) => {
const subdirectory = path.join(resourcePath, '_tmp', file.subdirectory);
if (!fs.existsSync(subdirectory)) {
fs.mkdirSync(subdirectory, {recursive: true});
}
const filePath = path.join(resourcePath, file.subdirectory, file.filepath);
const tempPath = path.join(tempResourcePath, file.subdirectory, file.filepath);
await conversionFunc(filePath, tempPath);
}));
console.log(`Replace ${type} with optimized versions`);
await Promise.all(files.map(async (file) => {
const filePath = path.join(resourcePath, file.subdirectory, file.filepath);
const tempPath = path.join(tempResourcePath, file.subdirectory, file.filepath);
await waitForFileReady(tempPath);
await fs.promises.rename(tempPath, filePath);
}));
if (fs.existsSync(tempResourcePath)) {
fs.rmSync(tempResourcePath, {recursive: true});
}
}
function getFiles(dirPath, extensions, arrayOfFiles) {
arrayOfFiles = arrayOfFiles || [];
if (!fs.existsSync(dirPath)) {
return;
}
const files = fs.readdirSync(dirPath);
files.forEach(function (file) {
if (fs.statSync(dirPath + "/" + file).isDirectory()) {
arrayOfFiles = getFiles(dirPath + "/" + file, extensions, arrayOfFiles);
} else if (extensions.find(e => file.indexOf(e) > -1)) {
const subdirectory = dirPath.replace(resourcePath, '');
arrayOfFiles.push({filepath: file, subdirectory: subdirectory});
}
})
return arrayOfFiles;
}
function zipDirectory(sourceDir, outPath) {
const archive = new ZipArchive({zlib: {level: 9}});
const stream = fs.createWriteStream(outPath);
return new Promise(async (resolve, reject) => {
archive
.directory(sourceDir, false)
.on('error', err => reject(err))
.pipe(stream)
;
stream.on('close', () => resolve());
await archive.finalize();
});
}
async function waitForFileReady(filePath, maxWaitTime = 5000) {
const startTime = Date.now();
let lastSize = 0;
let stableCount = 0;
while (Date.now() - startTime < maxWaitTime) {
try {
const stat = await fs.promises.stat(filePath);
// If file size hasn't changed for 2 checks, it's ready
if (stat.size === lastSize) {
stableCount++;
if (stableCount >= 2) return true;
} else {
stableCount = 0;
}
lastSize = stat.size;
await new Promise(resolve => setTimeout(resolve, 100));
} catch (error) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
return true;
}