add build script
parent
da7cef9cda
commit
f00514a562
|
@ -10,10 +10,10 @@ const __filename = fileURLToPath(import.meta.url)
|
||||||
const __dirname = path.dirname(__filename)
|
const __dirname = path.dirname(__filename)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 脚本执行之前的操作
|
// 脚本执行之前的操作
|
||||||
var iosUrl = "https://apps.apple.com/app/legend-of-mushroom/id6475333787"
|
var iosUrl = "https://apps.apple.com/us/app/top-heroes/id6450953550"
|
||||||
var androidUrl = "https://play.google.com/store/apps/details?id=com.mxdzzus.google"
|
var androidUrl = "https://play.google.com/store/apps/details?id=com.greenmushroom.boomblitz.gp&hl=en_US"
|
||||||
|
|
||||||
|
|
||||||
removeDist()
|
removeDist()
|
||||||
createDist()
|
createDist()
|
||||||
|
@ -67,9 +67,17 @@ async function startScript() {
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
// zip包
|
// zip包
|
||||||
zipChannel: ['facebook', 'google', 'tiktok', 'vungle', 'liftoff'],
|
zipChannel: ['facebook', 'google', 'tiktok',
|
||||||
|
'vungle', 'liftoff'
|
||||||
|
],
|
||||||
// html包
|
// html包
|
||||||
htmlChannel: ['applovin', 'unity', 'appier', 'ironsource', 'mintegral', 'moloco'],
|
htmlChannel: ['applovin',
|
||||||
|
|
||||||
|
'unity',
|
||||||
|
'appier', 'ironsource',
|
||||||
|
|
||||||
|
'mintegral', 'moloco'
|
||||||
|
],
|
||||||
outputPrefix: ''
|
outputPrefix: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,205 @@
|
||||||
|
const sharp = require('sharp');
|
||||||
|
const fs = require('fs').promises;
|
||||||
|
const path = require('path');
|
||||||
|
const ffmpeg = require('fluent-ffmpeg');
|
||||||
|
|
||||||
|
// 支持的图片格式
|
||||||
|
const IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.webp'];
|
||||||
|
const AUDIO_EXTENSIONS = ['.mp3'];
|
||||||
|
|
||||||
|
// 压缩配置
|
||||||
|
const COMPRESSION_OPTIONS = {
|
||||||
|
jpeg: {
|
||||||
|
quality: 60, // 降低质量以获得更高压缩率
|
||||||
|
mozjpeg: true, // 使用 mozjpeg 压缩
|
||||||
|
chromaSubsampling: '4:2:0' // 更激进的色度采样
|
||||||
|
},
|
||||||
|
png: {
|
||||||
|
quality: 60, // 降低质量
|
||||||
|
compressionLevel: 9, // 最高压缩级别
|
||||||
|
palette: true, // 使用调色板模式
|
||||||
|
effort: 10, // 最大压缩效果
|
||||||
|
colors: 128 // 减少调色板颜色数量以增加压缩率
|
||||||
|
},
|
||||||
|
webp: {
|
||||||
|
quality: 60, // 降低质量
|
||||||
|
effort: 6, // 最高压缩效果
|
||||||
|
lossless: false, // 有损压缩
|
||||||
|
nearLossless: false // 关闭近无损压缩
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化文件大小
|
||||||
|
function formatFileSize(bytes) {
|
||||||
|
if (bytes < 1024) return bytes + ' B';
|
||||||
|
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB';
|
||||||
|
return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function processDirectory(inputDir) {
|
||||||
|
try {
|
||||||
|
const items = await fs.readdir(inputDir);
|
||||||
|
let totalOriginalSize = 0;
|
||||||
|
let totalCompressedSize = 0;
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
const filePath = path.join(inputDir, item);
|
||||||
|
const stats = await fs.stat(filePath);
|
||||||
|
|
||||||
|
if (stats.isDirectory()) {
|
||||||
|
await processDirectory(filePath);
|
||||||
|
} else if (stats.isFile()) {
|
||||||
|
const ext = path.extname(item).toLowerCase();
|
||||||
|
const originalSize = stats.size;
|
||||||
|
|
||||||
|
if (IMAGE_EXTENSIONS.includes(ext)) {
|
||||||
|
await compressImage(filePath);
|
||||||
|
} else if (AUDIO_EXTENSIONS.includes(ext)) {
|
||||||
|
await compressAudio(filePath);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const compressedStats = await fs.stat(filePath);
|
||||||
|
const compressedSize = compressedStats.size;
|
||||||
|
|
||||||
|
totalOriginalSize += originalSize;
|
||||||
|
totalCompressedSize += compressedSize;
|
||||||
|
|
||||||
|
const savedSize = originalSize - compressedSize;
|
||||||
|
const savingPercentage = ((savedSize / originalSize) * 100).toFixed(2);
|
||||||
|
|
||||||
|
console.log(`已压缩: ${filePath}`);
|
||||||
|
console.log(` 原始大小: ${formatFileSize(originalSize)}`);
|
||||||
|
console.log(` 压缩后大小: ${formatFileSize(compressedSize)}`);
|
||||||
|
console.log(` 节省: ${formatFileSize(savedSize)} (${savingPercentage}%)`);
|
||||||
|
console.log('----------------------------------------');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalOriginalSize > 0) {
|
||||||
|
const totalSaved = totalOriginalSize - totalCompressedSize;
|
||||||
|
const totalSavingPercentage = ((totalSaved / totalOriginalSize) * 100).toFixed(2);
|
||||||
|
console.log(`\n当前目录 ${inputDir} 总计:`);
|
||||||
|
console.log(` 原始总大小: ${formatFileSize(totalOriginalSize)}`);
|
||||||
|
console.log(` 压缩后总大小: ${formatFileSize(totalCompressedSize)}`);
|
||||||
|
console.log(` 总共节省: ${formatFileSize(totalSaved)} (${totalSavingPercentage}%)\n`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('处理出错:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function compressImage(filePath) {
|
||||||
|
const ext = path.extname(filePath).toLowerCase();
|
||||||
|
const tempPath = filePath + '.temp';
|
||||||
|
const image = sharp(filePath);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取图片信息
|
||||||
|
const metadata = await image.metadata();
|
||||||
|
|
||||||
|
// 根据图片大小和类型选择压缩策略
|
||||||
|
switch (ext) {
|
||||||
|
case '.jpg':
|
||||||
|
case '.jpeg':
|
||||||
|
await image
|
||||||
|
.jpeg(COMPRESSION_OPTIONS.jpeg)
|
||||||
|
.toFile(tempPath);
|
||||||
|
break;
|
||||||
|
case '.png':
|
||||||
|
// 如果PNG是透明的,保持PNG格式
|
||||||
|
if (metadata.hasAlpha) {
|
||||||
|
await image
|
||||||
|
.png(COMPRESSION_OPTIONS.png)
|
||||||
|
.toFile(tempPath);
|
||||||
|
} else {
|
||||||
|
// 如果PNG不是透明的,转换为JPEG可能会得到更好的压缩效果
|
||||||
|
await image
|
||||||
|
.jpeg(COMPRESSION_OPTIONS.jpeg)
|
||||||
|
.toFile(tempPath);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '.webp':
|
||||||
|
await image
|
||||||
|
.webp(COMPRESSION_OPTIONS.webp)
|
||||||
|
.toFile(tempPath);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查压缩后的文件大小
|
||||||
|
const originalStats = await fs.stat(filePath);
|
||||||
|
const compressedStats = await fs.stat(tempPath);
|
||||||
|
|
||||||
|
// 只有当压缩后的文件更小时才替换
|
||||||
|
if (compressedStats.size < originalStats.size) {
|
||||||
|
await fs.unlink(filePath);
|
||||||
|
await fs.rename(tempPath, filePath);
|
||||||
|
console.log(` 压缩成功: ${formatFileSize(originalStats.size)} -> ${formatFileSize(compressedStats.size)}`);
|
||||||
|
} else {
|
||||||
|
// 如果压缩后反而更大,则删除临时文件保留原文件
|
||||||
|
await fs.unlink(tempPath);
|
||||||
|
console.log(` 跳过压缩: 原始大小 ${formatFileSize(originalStats.size)}, 压缩后 ${formatFileSize(compressedStats.size)}`);
|
||||||
|
console.log(` 原因: 压缩后文件更大,已保留原文件`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
try {
|
||||||
|
await fs.unlink(tempPath);
|
||||||
|
} catch (e) {
|
||||||
|
// 忽略临时文件删除失败的错误
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function compressAudio(filePath) {
|
||||||
|
const tempPath = filePath + '.temp';
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
ffmpeg(filePath)
|
||||||
|
.toFormat('mp3')
|
||||||
|
.audioBitrate(64) // 降低到64kbps以获得最大压缩
|
||||||
|
.audioChannels(1) // 转换为单声道
|
||||||
|
.audioFrequency(22050) // 降低采样率
|
||||||
|
.audioCodec('libmp3lame') // 使用LAME编码器
|
||||||
|
.addOptions([
|
||||||
|
'-q:a 9', // 最高压缩质量
|
||||||
|
'-compression_level 9' // 最高压缩级别
|
||||||
|
])
|
||||||
|
.on('end', async () => {
|
||||||
|
try {
|
||||||
|
// 检查压缩后的文件大小
|
||||||
|
const originalStats = await fs.stat(filePath);
|
||||||
|
const compressedStats = await fs.stat(tempPath);
|
||||||
|
|
||||||
|
// 只有当压缩后的文件更小时才替换
|
||||||
|
if (compressedStats.size < originalStats.size) {
|
||||||
|
await fs.unlink(filePath);
|
||||||
|
await fs.rename(tempPath, filePath);
|
||||||
|
console.log(` 压缩成功: ${formatFileSize(originalStats.size)} -> ${formatFileSize(compressedStats.size)}`);
|
||||||
|
} else {
|
||||||
|
await fs.unlink(tempPath);
|
||||||
|
console.log(` 跳过压缩: 原始大小 ${formatFileSize(originalStats.size)}, 压缩后 ${formatFileSize(compressedStats.size)}`);
|
||||||
|
console.log(` 原因: 压缩后文件更大,已保留原文件`);
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
})
|
||||||
|
.save(tempPath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置输入目录
|
||||||
|
const INPUT_DIR = './assets';
|
||||||
|
|
||||||
|
// 运行脚本
|
||||||
|
(async () => {
|
||||||
|
console.log('开始处理图片压缩...');
|
||||||
|
await processDirectory(INPUT_DIR);
|
||||||
|
console.log('图片压缩完成!');
|
||||||
|
})();
|
|
@ -1,6 +1,6 @@
|
||||||
// 填入对应的商店地址
|
// 填入对应的商店地址
|
||||||
var iosUrl = "https://apps.apple.com/app/legend-of-mushroom/id6475333787"
|
var iosUrl = "https://apps.apple.com/us/app/top-heroes/id6450953550"
|
||||||
var androidUrl = "https://play.google.com/store/apps/details?id=com.mxdzzus.google"
|
var androidUrl = "https://play.google.com/store/apps/details?id=com.greenmushroom.boomblitz.gp&hl=en_US"
|
||||||
|
|
||||||
|
|
||||||
class SoyooLifecyle {
|
class SoyooLifecyle {
|
||||||
|
|
Loading…
Reference in New Issue