forked from LeenkxTeam/LNXSDK
Update Files
This commit is contained in:
282
Kha/Tools/khamake/src/AssetConverter.ts
Normal file
282
Kha/Tools/khamake/src/AssetConverter.ts
Normal file
@ -0,0 +1,282 @@
|
||||
import {Callbacks} from './ProjectFile';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import {KhaExporter} from './Exporters/KhaExporter';
|
||||
import * as log from './log';
|
||||
import * as chokidar from 'chokidar';
|
||||
import * as crypto from 'crypto';
|
||||
import * as Throttle from 'promise-parallel-throttle';
|
||||
import { Options } from './Options';
|
||||
|
||||
export class AssetConverter {
|
||||
options: Options;
|
||||
exporter: KhaExporter;
|
||||
platform: string;
|
||||
assetMatchers: Array<{ match: string, options: any }>;
|
||||
watcher: fs.FSWatcher;
|
||||
|
||||
constructor(exporter: KhaExporter, options: Options, assetMatchers: Array<{ match: string, options: any }>) {
|
||||
this.exporter = exporter;
|
||||
this.options = options;
|
||||
this.platform = options.target;
|
||||
this.assetMatchers = assetMatchers;
|
||||
}
|
||||
|
||||
close(): void {
|
||||
if (this.watcher) this.watcher.close();
|
||||
}
|
||||
|
||||
static replacePattern(pattern: string, name: string, fileinfo: path.ParsedPath, options: any, 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
|
||||
&& dirValue.length > 0 && dirValue[dirValue.length - 1] !== path.sep) {
|
||||
dirValue += path.sep;
|
||||
}
|
||||
if (options.namePathSeparator) {
|
||||
dirValue = dirValue.split(path.sep).join(options.namePathSeparator);
|
||||
}
|
||||
|
||||
const dirRegex = dirValue === ''
|
||||
? /{dir}\//g
|
||||
: /{dir}/g;
|
||||
|
||||
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} {
|
||||
let nameValue = fileinfo.name;
|
||||
|
||||
let destination = fileinfo.name;
|
||||
|
||||
if (options.md5sum) {
|
||||
let data = fs.readFileSync(path.join(fileinfo.dir, fileinfo.base));
|
||||
let md5sum = crypto.createHash('md5').update(data).digest('hex'); // TODO yield generateMd5Sum(file);
|
||||
destination += '_' + md5sum;
|
||||
}
|
||||
if ((keepextension || options.noprocessing) && (!options.destination || options.destination.indexOf('{ext}') < 0)) {
|
||||
destination += fileinfo.ext;
|
||||
}
|
||||
|
||||
if (options.destination) {
|
||||
destination = AssetConverter.replacePattern(options.destination, destination, fileinfo, options, from);
|
||||
}
|
||||
|
||||
if (options.destinationCallback) {
|
||||
destination = options.destinationCallback(destination);
|
||||
}
|
||||
|
||||
if (keepextension && (!options.name || options.name.indexOf('{ext}') < 0)) {
|
||||
nameValue += fileinfo.ext;
|
||||
}
|
||||
|
||||
if (options.name) {
|
||||
nameValue = AssetConverter.replacePattern(options.name, nameValue, fileinfo, options, from);
|
||||
}
|
||||
|
||||
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 }[]> {
|
||||
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[] = [];
|
||||
this.watcher = chokidar.watch(match, { ignored: /[\/\\]\.(svn|git|DS_Store)/, persistent: watch, followSymlinks: false });
|
||||
|
||||
const onFileChange = async (file: string) => {
|
||||
const fileinfo = path.parse(file);
|
||||
let outPath = fileinfo.name;
|
||||
// with subfolders
|
||||
if (options.destination) {
|
||||
const from = path.resolve(options.baseDir, '..');
|
||||
outPath = AssetConverter.replacePattern(options.destination, fileinfo.name, fileinfo, options, from);
|
||||
}
|
||||
log.info('Reexporting ' + outPath + fileinfo.ext);
|
||||
switch (fileinfo.ext) {
|
||||
case '.png':
|
||||
case '.jpg':
|
||||
case '.jpeg':
|
||||
case '.hdr': {}
|
||||
await this.exporter.copyImage(this.platform, file, outPath, {}, {});
|
||||
break;
|
||||
|
||||
case '.ogg':
|
||||
case '.mp3':
|
||||
case '.flac':
|
||||
case '.wav': {
|
||||
await this.exporter.copySound(this.platform, file, outPath, {});
|
||||
break;
|
||||
}
|
||||
|
||||
case '.mp4':
|
||||
case '.webm':
|
||||
case '.mov':
|
||||
case '.wmv':
|
||||
case '.avi': {
|
||||
await this.exporter.copyVideo(this.platform, file, outPath, {});
|
||||
break;
|
||||
}
|
||||
|
||||
case '.ttf':
|
||||
await this.exporter.copyFont(this.platform, file, outPath, {});
|
||||
break;
|
||||
|
||||
default:
|
||||
await this.exporter.copyBlob(this.platform, file, outPath + fileinfo.ext, {});
|
||||
}
|
||||
for (let callback of Callbacks.postAssetReexporting) {
|
||||
callback(outPath + fileinfo.ext);
|
||||
}
|
||||
};
|
||||
|
||||
this.watcher.on('add', (file: string) => {
|
||||
if (ready) {
|
||||
onFileChange(file);
|
||||
}
|
||||
else {
|
||||
files.push(file);
|
||||
}
|
||||
});
|
||||
if (watch) {
|
||||
this.watcher.on('change', (file: string) => {
|
||||
if (ready) {
|
||||
onFileChange(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.watcher.on('ready', async () => {
|
||||
ready = true;
|
||||
let parsedFiles: { name: string, from: string, type: string, files: string[], file_sizes: number[], original_width: number, original_height: number, readable: boolean }[] = [];
|
||||
let cache: any = {};
|
||||
let cachePath = path.join(temp, 'cache.json');
|
||||
if (fs.existsSync(cachePath)) {
|
||||
cache = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
|
||||
}
|
||||
|
||||
const self = this;
|
||||
|
||||
async function convertAsset( 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();
|
||||
switch (ext) {
|
||||
case '.png':
|
||||
case '.jpg':
|
||||
case '.jpeg':
|
||||
case '.hdr': {
|
||||
let exportInfo = AssetConverter.createExportInfo(fileinfo, false, options, self.exporter.options.from);
|
||||
let images: { files: string[], sizes: number[] };
|
||||
if (options.noprocessing) {
|
||||
images = await self.exporter.copyBlob(self.platform, file, exportInfo.destination, options);
|
||||
}
|
||||
else {
|
||||
images = await self.exporter.copyImage(self.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 });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '.ogg':
|
||||
case '.mp3':
|
||||
case '.flac':
|
||||
case '.wav': {
|
||||
let exportInfo = AssetConverter.createExportInfo(fileinfo, false, options, self.exporter.options.from);
|
||||
let sounds: { files: string[], sizes: number[] };
|
||||
if (options.noprocessing) {
|
||||
sounds = await self.exporter.copyBlob(self.platform, file, exportInfo.destination, options);
|
||||
}
|
||||
else {
|
||||
sounds = await self.exporter.copySound(self.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.';
|
||||
}
|
||||
if (!options.notinlist) {
|
||||
parsedFiles.push({ name: exportInfo.name, from: file, type: 'sound', files: sounds.files, file_sizes: sounds.sizes, original_width: undefined, original_height: undefined, readable: undefined });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '.ttf': {
|
||||
let exportInfo = AssetConverter.createExportInfo(fileinfo, false, options, self.exporter.options.from);
|
||||
let fonts: { files: string[], sizes: number[] };
|
||||
if (options.noprocessing) {
|
||||
fonts = await self.exporter.copyBlob(self.platform, file, exportInfo.destination, options);
|
||||
}
|
||||
else {
|
||||
fonts = await self.exporter.copyFont(self.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 });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '.mp4':
|
||||
case '.webm':
|
||||
case '.mov':
|
||||
case '.wmv':
|
||||
case '.avi': {
|
||||
let exportInfo = AssetConverter.createExportInfo(fileinfo, false, options, self.exporter.options.from);
|
||||
let videos: { files: string[], sizes: number[] };
|
||||
if (options.noprocessing) {
|
||||
videos = await self.exporter.copyBlob(self.platform, file, exportInfo.destination, options);
|
||||
}
|
||||
else {
|
||||
videos = await self.exporter.copyVideo(self.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.');
|
||||
}
|
||||
if (!options.notinlist) {
|
||||
parsedFiles.push({ name: exportInfo.name, from: file, type: 'video', files: videos.files, file_sizes: videos.sizes, original_width: undefined, original_height: undefined, readable: undefined });
|
||||
}
|
||||
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);
|
||||
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 });
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.parallelAssetConversion !== 0) {
|
||||
let todo = files.map((file, index) => {
|
||||
return async () => {
|
||||
await convertAsset(file, index);
|
||||
};
|
||||
});
|
||||
|
||||
let processes = this.options.parallelAssetConversion === -1
|
||||
? require('os').cpus().length - 1
|
||||
: this.options.parallelAssetConversion;
|
||||
|
||||
await Throttle.all(todo, {
|
||||
maxInProgress: processes,
|
||||
});
|
||||
} else {
|
||||
let index = 0;
|
||||
for (let file of files) {
|
||||
await convertAsset(file, index);
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fs.ensureDirSync(temp);
|
||||
fs.writeFileSync(cachePath, JSON.stringify(cache), { encoding: 'utf8'});
|
||||
resolve(parsedFiles);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async run(watch: boolean, temp: string): Promise<{ name: string, from: string, type: string, files: string[], file_sizes: number[], original_width: number, original_height: number, readable: boolean }[]> {
|
||||
let files: { name: string, from: string, type: string, files: string[], file_sizes: number[], original_width: number, original_height: number, readable: boolean }[] = [];
|
||||
for (let matcher of this.assetMatchers) {
|
||||
files = files.concat(await this.watch(watch, matcher.match, temp, matcher.options));
|
||||
}
|
||||
return files;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user