This commit is contained in:
Dante
2026-05-21 23:40:20 -07:00
parent 3e2915dff7
commit 877a69d844
5737 changed files with 29796 additions and 1589684 deletions

View File

@ -7,15 +7,16 @@ import * as chokidar from 'chokidar';
import * as crypto from 'crypto';
import * as Throttle from 'promise-parallel-throttle';
import { Options } from './Options';
import { AssetMatcher, AssetMatcherOptions } from './Project';
export class AssetConverter {
options: Options;
exporter: KhaExporter;
platform: string;
assetMatchers: Array<{ match: string, options: any }>;
assetMatchers: Array<AssetMatcher>;
watcher: fs.FSWatcher;
constructor(exporter: KhaExporter, options: Options, assetMatchers: Array<{ match: string, options: any }>) {
constructor(exporter: KhaExporter, options: Options, assetMatchers: Array<AssetMatcher>) {
this.exporter = exporter;
this.options = options;
this.platform = options.target;
@ -26,7 +27,7 @@ export class AssetConverter {
if (this.watcher) this.watcher.close();
}
static replacePattern(pattern: string, name: string, fileinfo: path.ParsedPath, options: any, from: string) {
static replacePattern(pattern: string, name: string, fileinfo: path.ParsedPath, options: AssetMatcherOptions, from: string) {
let basePath: string = options.nameBaseDir ? path.join(from, options.nameBaseDir) : from;
let dirValue: string = path.relative(basePath, fileinfo.dir);
if (basePath.length > 0 && basePath[basePath.length - 1] === path.sep
@ -44,7 +45,7 @@ export class AssetConverter {
return pattern.replace(/{name}/g, name).replace(/{ext}/g, fileinfo.ext).replace(dirRegex, dirValue);
}
static createExportInfo(fileinfo: path.ParsedPath, keepextension: boolean, options: any, from: string): {name: string, destination: string} {
static createExportInfo(fileinfo: path.ParsedPath, keepextension: boolean, options: AssetMatcherOptions, from: string): {name: string, destination: string} {
let nameValue = fileinfo.name;
let destination = fileinfo.name;
@ -77,7 +78,28 @@ export class AssetConverter {
return {name: nameValue, destination: destination};
}
watch(watch: boolean, match: string, temp: string, options: any): Promise<{ name: string, from: string, type: string, files: string[], file_sizes: number[], original_width: number, original_height: number, readable: boolean }[]> {
canDecodeFormat(ext: string): boolean {
// without ffmpeg we need to encode mp3 and ogg files
const hasFFmpeg = !!this.options.ffmpeg;
const hasFFmpegOgg = this.options.ogg?.includes('ffmpeg') ?? false;
const hasFFmpegMp3 = this.options.mp3?.includes('ffmpeg') ?? false;
const hasFFmpegAac = this.options.aac?.includes('ffmpeg') ?? false;
switch (ext) {
case '.wav':
return true;
case '.ogg':
return hasFFmpeg || (hasFFmpegOgg && (hasFFmpegMp3 || hasFFmpegAac));
case '.mp3':
// lame can decode mp3, so we only need ogg ffmpeg encoder
return hasFFmpeg || (hasFFmpegOgg && !!this.options.mp3);
case '.flac':
return hasFFmpeg;
default:
return false;
}
}
watch(watch: boolean, match: string, temp: string, options: AssetMatcherOptions): Promise<{ name: string, from: string, type: string, files: string[], file_sizes: number[], original_width: number, original_height: number, readable: boolean }[]> {
return new Promise<{ name: string, from: string, type: string, files: string[], file_sizes: number[], original_width: number, original_height: number, readable: boolean }[]>((resolve, reject) => {
let ready = false;
let files: string[] = [];
@ -88,11 +110,15 @@ export class AssetConverter {
let outPath = fileinfo.name;
// with subfolders
if (options.destination) {
const from = path.resolve(options.baseDir, '..');
// remove trailing slash
const nameBaseDir = options.nameBaseDir.replace(/\/$/, '');
const lastIndex = options.baseDir.lastIndexOf(nameBaseDir)
const from = path.resolve(options.baseDir.substring(0, lastIndex));
outPath = AssetConverter.replacePattern(options.destination, fileinfo.name, fileinfo, options, from);
}
log.info('Reexporting ' + outPath + fileinfo.ext);
switch (fileinfo.ext) {
const ext = fileinfo.ext.toLowerCase();
log.info('Reexporting ' + outPath + ext);
switch (ext) {
case '.png':
case '.jpg':
case '.jpeg':
@ -104,6 +130,9 @@ export class AssetConverter {
case '.mp3':
case '.flac':
case '.wav': {
if (!this.canDecodeFormat(ext)) {
log.error(`Error: ${fileinfo.base} should be in wav format, or use \`--ffmpeg path/to/ffmpeg\` option to convert ogg/mp3/flac files`);
}
await this.exporter.copySound(this.platform, file, outPath, {});
break;
}
@ -122,10 +151,10 @@ export class AssetConverter {
break;
default:
await this.exporter.copyBlob(this.platform, file, outPath + fileinfo.ext, {});
await this.exporter.copyBlob(this.platform, file, outPath + ext, {});
}
for (let callback of Callbacks.postAssetReexporting) {
callback(outPath + fileinfo.ext);
callback(outPath + ext);
}
};
@ -153,9 +182,7 @@ export class AssetConverter {
cache = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
}
const self = this;
async function convertAsset( file: string, index: number ) {
const convertAsset = async (file: string, index: number) => {
let fileinfo = path.parse(file);
log.info('Exporting asset ' + (index + 1) + ' of ' + files.length + ' (' + fileinfo.base + ').');
const ext = fileinfo.ext.toLowerCase();
@ -164,13 +191,13 @@ export class AssetConverter {
case '.jpg':
case '.jpeg':
case '.hdr': {
let exportInfo = AssetConverter.createExportInfo(fileinfo, false, options, self.exporter.options.from);
let exportInfo = AssetConverter.createExportInfo(fileinfo, false, options, this.exporter.options.from);
let images: { files: string[], sizes: number[] };
if (options.noprocessing) {
images = await self.exporter.copyBlob(self.platform, file, exportInfo.destination, options);
images = await this.exporter.copyBlob(this.platform, file, exportInfo.destination, options);
}
else {
images = await self.exporter.copyImage(self.platform, file, exportInfo.destination, options, cache);
images = await this.exporter.copyImage(this.platform, file, exportInfo.destination, options, cache);
}
if (!options.notinlist) {
parsedFiles.push({ name: exportInfo.name, from: file, type: 'image', files: images.files, file_sizes: images.sizes, original_width: options.original_width, original_height: options.original_height, readable: options.readable });
@ -181,13 +208,17 @@ export class AssetConverter {
case '.mp3':
case '.flac':
case '.wav': {
let exportInfo = AssetConverter.createExportInfo(fileinfo, false, options, self.exporter.options.from);
let exportInfo = AssetConverter.createExportInfo(fileinfo, false, options, this.exporter.options.from);
let sounds: { files: string[], sizes: number[] };
if (options.noprocessing) {
sounds = await self.exporter.copyBlob(self.platform, file, exportInfo.destination, options);
sounds = await this.exporter.copyBlob(this.platform, file, exportInfo.destination, options);
}
else {
sounds = await self.exporter.copySound(self.platform, file, exportInfo.destination, options);
if (!this.canDecodeFormat(ext)) {
log.error(`Error: ${fileinfo.base} should be in wav format, or use \`--ffmpeg\` option to convert ogg/mp3/flac files`);
process.exit(1);
}
sounds = await this.exporter.copySound(this.platform, file, exportInfo.destination, options);
}
if (sounds.files.length === 0) {
throw 'Audio file ' + file + ' could not be exported, you have to specify a path to ffmpeg.';
@ -198,13 +229,13 @@ export class AssetConverter {
break;
}
case '.ttf': {
let exportInfo = AssetConverter.createExportInfo(fileinfo, false, options, self.exporter.options.from);
let exportInfo = AssetConverter.createExportInfo(fileinfo, false, options, this.exporter.options.from);
let fonts: { files: string[], sizes: number[] };
if (options.noprocessing) {
fonts = await self.exporter.copyBlob(self.platform, file, exportInfo.destination, options);
fonts = await this.exporter.copyBlob(this.platform, file, exportInfo.destination, options);
}
else {
fonts = await self.exporter.copyFont(self.platform, file, exportInfo.destination, options);
fonts = await this.exporter.copyFont(this.platform, file, exportInfo.destination, options);
}
if (!options.notinlist) {
parsedFiles.push({ name: exportInfo.name, from: file, type: 'font', files: fonts.files, file_sizes: fonts.sizes, original_width: undefined, original_height: undefined, readable: undefined });
@ -216,13 +247,13 @@ export class AssetConverter {
case '.mov':
case '.wmv':
case '.avi': {
let exportInfo = AssetConverter.createExportInfo(fileinfo, false, options, self.exporter.options.from);
let exportInfo = AssetConverter.createExportInfo(fileinfo, false, options, this.exporter.options.from);
let videos: { files: string[], sizes: number[] };
if (options.noprocessing) {
videos = await self.exporter.copyBlob(self.platform, file, exportInfo.destination, options);
videos = await this.exporter.copyBlob(this.platform, file, exportInfo.destination, options);
}
else {
videos = await self.exporter.copyVideo(self.platform, file, exportInfo.destination, options);
videos = await this.exporter.copyVideo(this.platform, file, exportInfo.destination, options);
}
if (videos.files.length === 0) {
log.error('Video file ' + file + ' could not be exported, you have to specify a path to ffmpeg.');
@ -233,8 +264,8 @@ export class AssetConverter {
break;
}
default: {
let exportInfo = AssetConverter.createExportInfo(fileinfo, true, options, self.exporter.options.from);
let blobs = await self.exporter.copyBlob(self.platform, file, exportInfo.destination, options);
let exportInfo = AssetConverter.createExportInfo(fileinfo, true, options, this.exporter.options.from);
let blobs = await this.exporter.copyBlob(this.platform, file, exportInfo.destination, options);
if (!options.notinlist) {
parsedFiles.push({ name: exportInfo.name, from: file, type: 'blob', files: blobs.files, file_sizes: blobs.sizes, original_width: undefined, original_height: undefined, readable: undefined });
}