kar-to-ass
Version:
Converts midi/kar karaoke files to ass format
146 lines • 5.98 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertKarToAss = exports.convertKarStringToAss = exports.parseKar = void 0;
const utils_1 = require("./utils");
const ass_stringify_1 = __importDefault(require("ass-stringify"));
const uuid_1 = require("uuid");
const commander_1 = __importDefault(require("commander"));
const kar_1 = require("./kar");
const ass = require('./assTemplate');
const pjson = require('../package.json');
function generateASSLine(line, sectionId, options) {
const offset = options.offset || 0;
const syncType = options.progressive ? 'kf' : 'k';
const dialogue = utils_1.clone(ass.dialogue);
const comment = utils_1.clone(ass.dialogue);
const lineStart = line[0].timeUs + 1e4 * (offset - ass.previewTime);
const lineStartComment = line[0].timeUs + 1e4 * offset;
const lineEnd = line[line.length - 1].timeUs + 1e4 * (offset + ass.postviewTime);
const correction = lineStart < 0 ? lineStart : 0;
dialogue.value.Style = sectionId;
dialogue.value.Start = utils_1.usToAss(lineStart - correction);
dialogue.value.End = utils_1.usToAss(lineEnd - correction);
dialogue.value.Text = writeLineAsAss(line, syncType);
dialogue.value.Effect = 'fx';
comment.value.Style = sectionId;
comment.value.Start = utils_1.usToAss(lineStartComment - correction);
comment.value.End = utils_1.usToAss(lineEnd - correction);
comment.value.Text = writeLineAsAss(line, syncType, true);
comment.value.Effect = 'karaoke';
comment.key = 'Comment';
return {
dialogue,
comment
};
}
function parseKar(fileContent) {
return kar_1.parseKar(fileContent);
}
exports.parseKar = parseKar;
function convertKarStringToAss(fileContent, options) {
return convertKarToAss(parseKar(fileContent), options);
}
exports.convertKarStringToAss = convertKarStringToAss;
function convertKarToAss(kar, options) {
const defaultStyle = ass.styles.body.filter(style => style.key == 'Style')[0];
const dialogues = [];
const comments = [];
const styles = utils_1.clone(ass.styles);
const script = utils_1.clone(ass.dialogue);
script.value.Effect = ass.scriptFX;
script.value.Text = ass.script;
script.key = 'Comment';
comments.push(script);
for (const track of kar.tracks) {
const sectionId = uuid_1.v4().replace(/-/g, '').substring(0, 16);
const sectionStyle = utils_1.clone(defaultStyle);
sectionStyle.value.Name = sectionId;
styles.body.push(sectionStyle);
for (const line of splitLines(track)) {
const ASSLines = generateASSLine(line, sectionId, options);
comments.push(utils_1.clone(ASSLines.comment));
dialogues.push(utils_1.clone(ASSLines.dialogue));
}
}
const scriptInfo = utils_1.clone(ass.scriptInfo);
scriptInfo.body[0].value = scriptInfo.body[0].value.replace('%version', pjson.version);
scriptInfo.body[1].value = scriptInfo.body[1].value.replace('%options', JSON.stringify(options));
const events = utils_1.clone(ass.events);
events.body = events.body.concat(comments, dialogues);
return ass_stringify_1.default([scriptInfo, styles, events]);
}
exports.convertKarToAss = convertKarToAss;
function splitLines(track) {
const lines = [];
let tempLine = [];
for (const event of track) {
if (event.type === 'EndOfLine' && tempLine.length > 0) {
tempLine.push(event);
lines.push(tempLine);
tempLine = [];
}
else if (event.type === 'Lyrics') {
tempLine.push(event);
}
}
return lines;
}
function writeLineAsAss(line, syncType, comment) {
if (line.length == 0)
return '';
const realPreview = Math.max(Math.min(ass.previewTime, line[0].timeUs * 1e-4), 0);
const resArray = [];
if (!comment)
resArray.push(`{\\k${realPreview}\\fad(300,200)}`);
for (let i = 0; i < line.length - 1; i++) {
resArray.push(`${line[i].text.startsWith(' ') ? ' ' : ''}{\\${syncType}${Math.round(1e-4 * (line[i + 1].timeUs - line[i].timeUs))}}${line[i].text.trim()}${(line[i].text.endsWith(' ') ? ' ' : '')}`);
}
return resArray.join('');
}
async function mainCLI() {
const argv = parseArgs();
if (!argv.in)
throw '--in argument is required';
const karFile = argv.in;
const correctionString = argv.correction;
const outFile = argv.out;
const correction = (correctionString && !isNaN(+correctionString))
? +correctionString : 0;
const options = {
offset: correction,
progressive: argv.progressive || false,
};
if (!await utils_1.asyncExists(karFile))
throw `File ${karFile} does not exist`;
const fileContent = await utils_1.asyncReadFile(karFile);
const kar = parseKar(fileContent);
const assString = convertKarToAss(kar, options);
if (!outFile) {
return assString;
}
else {
await utils_1.asyncWriteFile(outFile, assString);
return '';
}
}
function parseArgs() {
const argv = process.argv.filter(e => e !== '--');
return commander_1.default
.command('kar-to-ass')
.description('Converts midi/kar files to ass format')
.version(pjson.version)
.option('-i, --in <karFile> (required)', 'the file to read data from')
.option('-o, --out <assFile>', 'if set, saves the converted data there instead of printing it on stdout')
.option('-c, --correction <cs>', 'offsets said amount of cs on each line', 0)
.option('-p, --progressive', 'use kf instead of k ')
.parse(argv);
}
if (require.main === module)
mainCLI()
.then(data => console.log(data))
.catch(err => console.error(err));
//# sourceMappingURL=index.js.map