@utilix-tech/sdk 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +24 -5
- package/dist/{chunk-TINQFTLO.js → chunk-66QHV5KW.js} +178 -1
- package/dist/{chunk-TLRSQHMP.js → chunk-HZIS2DMJ.js} +155 -1
- package/dist/{data-D8XTI7Du.d.cts → data-XRb0D7Zn.d.cts} +75 -2
- package/dist/{data-D8XTI7Du.d.ts → data-XRb0D7Zn.d.ts} +75 -2
- package/dist/index.cjs +331 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/tools/data.cjs +177 -0
- package/dist/tools/data.d.cts +1 -1
- package/dist/tools/data.d.ts +1 -1
- package/dist/tools/data.js +1 -1
- package/dist/tools/units.cjs +154 -0
- package/dist/tools/units.d.cts +1 -1
- package/dist/tools/units.d.ts +1 -1
- package/dist/tools/units.js +1 -1
- package/dist/{units-KrjRyu0w.d.cts → units-DAoSM2g8.d.cts} +101 -2
- package/dist/{units-KrjRyu0w.d.ts → units-DAoSM2g8.d.ts} +101 -2
- package/package.json +1 -1
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* - env-parser : parseEnv, envToJson, envToExport, validateEnvKey
|
|
13
13
|
* - env-diff-checker: diffEnvExample
|
|
14
14
|
* - ndjson-formatter: validateNdjson, minifyNdjson, formatNdjson, ndjsonToJsonArray, jsonArrayToNdjson
|
|
15
|
+
* - subtitle-converter: parseSubtitleTimestamp, formatSubtitleTimestamp, detectSubtitleFormat, parseSubtitles, serializeSubtitles, convertSubtitles, shiftSubtitles
|
|
15
16
|
*/
|
|
16
17
|
interface ValidationResult {
|
|
17
18
|
valid: boolean;
|
|
@@ -210,7 +211,69 @@ declare function ndjsonToJsonArray(input: string): string | {
|
|
|
210
211
|
declare function jsonArrayToNdjson(input: string): string | {
|
|
211
212
|
error: string;
|
|
212
213
|
};
|
|
214
|
+
type SubtitleFormat = "srt" | "vtt";
|
|
215
|
+
interface SubtitleCue {
|
|
216
|
+
index: number;
|
|
217
|
+
/** Cue identifier line, when the source had one (WebVTT only). */
|
|
218
|
+
id?: string;
|
|
219
|
+
/** Start time in seconds. */
|
|
220
|
+
start: number;
|
|
221
|
+
/** End time in seconds. */
|
|
222
|
+
end: number;
|
|
223
|
+
/** WebVTT cue settings that followed the timestamp (e.g. `line:0 align:start`). */
|
|
224
|
+
settings?: string;
|
|
225
|
+
text: string;
|
|
226
|
+
}
|
|
227
|
+
interface ParsedSubtitles {
|
|
228
|
+
format: SubtitleFormat;
|
|
229
|
+
cues: SubtitleCue[];
|
|
230
|
+
cueCount: number;
|
|
231
|
+
/** End time of the last cue, in seconds. */
|
|
232
|
+
duration: number;
|
|
233
|
+
warnings: string[];
|
|
234
|
+
}
|
|
235
|
+
interface ConvertedSubtitles {
|
|
236
|
+
format: SubtitleFormat;
|
|
237
|
+
sourceFormat: SubtitleFormat;
|
|
238
|
+
output: string;
|
|
239
|
+
cueCount: number;
|
|
240
|
+
duration: number;
|
|
241
|
+
warnings: string[];
|
|
242
|
+
}
|
|
243
|
+
/** Parse `HH:MM:SS,mmm`, `HH:MM:SS.mmm`, or WebVTT's `MM:SS.mmm` into seconds. */
|
|
244
|
+
declare function parseSubtitleTimestamp(raw: string): number | null;
|
|
245
|
+
/** Format seconds as `HH:MM:SS,mmm` (SRT) or `HH:MM:SS.mmm` (WebVTT). */
|
|
246
|
+
declare function formatSubtitleTimestamp(totalSeconds: number, format: SubtitleFormat): string;
|
|
247
|
+
/** Auto-detect the source format from the WEBVTT signature or timestamp separator. */
|
|
248
|
+
declare function detectSubtitleFormat(input: string): SubtitleFormat;
|
|
249
|
+
/**
|
|
250
|
+
* Parse an SRT or WebVTT file into normalised cues. The format is auto-detected
|
|
251
|
+
* unless you pass one. Malformed blocks are reported as warnings rather than
|
|
252
|
+
* throwing, so a mostly-good file still converts.
|
|
253
|
+
*/
|
|
254
|
+
declare function parseSubtitles(input: string, format?: SubtitleFormat): ParsedSubtitles | {
|
|
255
|
+
error: string;
|
|
256
|
+
};
|
|
257
|
+
/** Serialise parsed cues back into SRT or WebVTT text. */
|
|
258
|
+
declare function serializeSubtitles(cues: SubtitleCue[], format: SubtitleFormat): string;
|
|
259
|
+
/**
|
|
260
|
+
* Convert a subtitle file to the target format. Converting WebVTT to SRT drops
|
|
261
|
+
* cue identifiers, cue settings, and NOTE/STYLE/REGION blocks, since SRT has no
|
|
262
|
+
* equivalent for any of them; each drop is reported as a warning.
|
|
263
|
+
*/
|
|
264
|
+
declare function convertSubtitles(input: string, target: SubtitleFormat, sourceFormat?: SubtitleFormat): ConvertedSubtitles | {
|
|
265
|
+
error: string;
|
|
266
|
+
};
|
|
267
|
+
/**
|
|
268
|
+
* Shift every cue by a fixed offset in seconds, keeping the source format
|
|
269
|
+
* unless a target is given. Negative offsets clamp at zero, so a cue that would
|
|
270
|
+
* start before the video does starts at 00:00:00 instead.
|
|
271
|
+
*/
|
|
272
|
+
declare function shiftSubtitles(input: string, offsetSeconds: number, target?: SubtitleFormat): ConvertedSubtitles | {
|
|
273
|
+
error: string;
|
|
274
|
+
};
|
|
213
275
|
|
|
276
|
+
type data_ConvertedSubtitles = ConvertedSubtitles;
|
|
214
277
|
type data_CsvParseResult = CsvParseResult;
|
|
215
278
|
type data_EnvDiffError = EnvDiffError;
|
|
216
279
|
type data_EnvDiffResult = EnvDiffResult;
|
|
@@ -221,16 +284,22 @@ type data_NdjsonLineResult = NdjsonLineResult;
|
|
|
221
284
|
type data_NdjsonValidateResult = NdjsonValidateResult;
|
|
222
285
|
type data_ParseOptions = ParseOptions;
|
|
223
286
|
type data_ParsedIni = ParsedIni;
|
|
287
|
+
type data_ParsedSubtitles = ParsedSubtitles;
|
|
288
|
+
type data_SubtitleCue = SubtitleCue;
|
|
289
|
+
type data_SubtitleFormat = SubtitleFormat;
|
|
224
290
|
type data_TomlFormatResult = TomlFormatResult;
|
|
225
291
|
type data_ValidationResult = ValidationResult;
|
|
226
292
|
type data_XmlFormatResult = XmlFormatResult;
|
|
227
293
|
type data_XmlToJsonOptions = XmlToJsonOptions;
|
|
294
|
+
declare const data_convertSubtitles: typeof convertSubtitles;
|
|
228
295
|
declare const data_csvToJson: typeof csvToJson;
|
|
229
296
|
declare const data_detectDelimiter: typeof detectDelimiter;
|
|
297
|
+
declare const data_detectSubtitleFormat: typeof detectSubtitleFormat;
|
|
230
298
|
declare const data_diffEnvExample: typeof diffEnvExample;
|
|
231
299
|
declare const data_envToExport: typeof envToExport;
|
|
232
300
|
declare const data_envToJson: typeof envToJson;
|
|
233
301
|
declare const data_formatNdjson: typeof formatNdjson;
|
|
302
|
+
declare const data_formatSubtitleTimestamp: typeof formatSubtitleTimestamp;
|
|
234
303
|
declare const data_formatToml: typeof formatToml;
|
|
235
304
|
declare const data_formatXml: typeof formatXml;
|
|
236
305
|
declare const data_getSection: typeof getSection;
|
|
@@ -250,7 +319,11 @@ declare const data_ndjsonToJsonArray: typeof ndjsonToJsonArray;
|
|
|
250
319
|
declare const data_parseCsv: typeof parseCsv;
|
|
251
320
|
declare const data_parseEnv: typeof parseEnv;
|
|
252
321
|
declare const data_parseIni: typeof parseIni;
|
|
322
|
+
declare const data_parseSubtitleTimestamp: typeof parseSubtitleTimestamp;
|
|
323
|
+
declare const data_parseSubtitles: typeof parseSubtitles;
|
|
253
324
|
declare const data_parseXmlToObject: typeof parseXmlToObject;
|
|
325
|
+
declare const data_serializeSubtitles: typeof serializeSubtitles;
|
|
326
|
+
declare const data_shiftSubtitles: typeof shiftSubtitles;
|
|
254
327
|
declare const data_stringifyIni: typeof stringifyIni;
|
|
255
328
|
declare const data_toCsvString: typeof toCsvString;
|
|
256
329
|
declare const data_tomlToJson: typeof tomlToJson;
|
|
@@ -262,7 +335,7 @@ declare const data_validateXml: typeof validateXml;
|
|
|
262
335
|
declare const data_validateYaml: typeof validateYaml;
|
|
263
336
|
declare const data_xmlToJson: typeof xmlToJson;
|
|
264
337
|
declare namespace data {
|
|
265
|
-
export { type data_CsvParseResult as CsvParseResult, type data_EnvDiffError as EnvDiffError, type data_EnvDiffResult as EnvDiffResult, type data_EnvEntry as EnvEntry, type data_EnvParseResult as EnvParseResult, type data_IniSection as IniSection, type data_NdjsonLineResult as NdjsonLineResult, type data_NdjsonValidateResult as NdjsonValidateResult, type data_ParseOptions as ParseOptions, type data_ParsedIni as ParsedIni, type data_TomlFormatResult as TomlFormatResult, type data_ValidationResult as ValidationResult, type data_XmlFormatResult as XmlFormatResult, type data_XmlToJsonOptions as XmlToJsonOptions, data_csvToJson as csvToJson, data_detectDelimiter as detectDelimiter, data_diffEnvExample as diffEnvExample, data_envToExport as envToExport, data_envToJson as envToJson, data_formatNdjson as formatNdjson, data_formatToml as formatToml, data_formatXml as formatXml, data_getSection as getSection, data_getSectionNames as getSectionNames, data_getTomlStats as getTomlStats, data_getXmlStats as getXmlStats, data_getYamlStats as getYamlStats, data_iniToJson as iniToJson, data_jsonArrayToNdjson as jsonArrayToNdjson, data_jsonToIni as jsonToIni, data_jsonToToml as jsonToToml, data_jsonToXml as jsonToXml, data_minifyNdjson as minifyNdjson, data_minifyToml as minifyToml, data_minifyXml as minifyXml, data_ndjsonToJsonArray as ndjsonToJsonArray, data_parseCsv as parseCsv, data_parseEnv as parseEnv, data_parseIni as parseIni, data_parseXmlToObject as parseXmlToObject, data_stringifyIni as stringifyIni, data_toCsvString as toCsvString, data_tomlToJson as tomlToJson, data_validateEnvKey as validateEnvKey, data_validateIni as validateIni, data_validateNdjson as validateNdjson, data_validateToml as validateToml, data_validateXml as validateXml, data_validateYaml as validateYaml, data_xmlToJson as xmlToJson };
|
|
338
|
+
export { type data_ConvertedSubtitles as ConvertedSubtitles, type data_CsvParseResult as CsvParseResult, type data_EnvDiffError as EnvDiffError, type data_EnvDiffResult as EnvDiffResult, type data_EnvEntry as EnvEntry, type data_EnvParseResult as EnvParseResult, type data_IniSection as IniSection, type data_NdjsonLineResult as NdjsonLineResult, type data_NdjsonValidateResult as NdjsonValidateResult, type data_ParseOptions as ParseOptions, type data_ParsedIni as ParsedIni, type data_ParsedSubtitles as ParsedSubtitles, type data_SubtitleCue as SubtitleCue, type data_SubtitleFormat as SubtitleFormat, type data_TomlFormatResult as TomlFormatResult, type data_ValidationResult as ValidationResult, type data_XmlFormatResult as XmlFormatResult, type data_XmlToJsonOptions as XmlToJsonOptions, data_convertSubtitles as convertSubtitles, data_csvToJson as csvToJson, data_detectDelimiter as detectDelimiter, data_detectSubtitleFormat as detectSubtitleFormat, data_diffEnvExample as diffEnvExample, data_envToExport as envToExport, data_envToJson as envToJson, data_formatNdjson as formatNdjson, data_formatSubtitleTimestamp as formatSubtitleTimestamp, data_formatToml as formatToml, data_formatXml as formatXml, data_getSection as getSection, data_getSectionNames as getSectionNames, data_getTomlStats as getTomlStats, data_getXmlStats as getXmlStats, data_getYamlStats as getYamlStats, data_iniToJson as iniToJson, data_jsonArrayToNdjson as jsonArrayToNdjson, data_jsonToIni as jsonToIni, data_jsonToToml as jsonToToml, data_jsonToXml as jsonToXml, data_minifyNdjson as minifyNdjson, data_minifyToml as minifyToml, data_minifyXml as minifyXml, data_ndjsonToJsonArray as ndjsonToJsonArray, data_parseCsv as parseCsv, data_parseEnv as parseEnv, data_parseIni as parseIni, data_parseSubtitleTimestamp as parseSubtitleTimestamp, data_parseSubtitles as parseSubtitles, data_parseXmlToObject as parseXmlToObject, data_serializeSubtitles as serializeSubtitles, data_shiftSubtitles as shiftSubtitles, data_stringifyIni as stringifyIni, data_toCsvString as toCsvString, data_tomlToJson as tomlToJson, data_validateEnvKey as validateEnvKey, data_validateIni as validateIni, data_validateNdjson as validateNdjson, data_validateToml as validateToml, data_validateXml as validateXml, data_validateYaml as validateYaml, data_xmlToJson as xmlToJson };
|
|
266
339
|
}
|
|
267
340
|
|
|
268
|
-
export {
|
|
341
|
+
export { toCsvString as $, iniToJson as A, jsonArrayToNdjson as B, type ConvertedSubtitles as C, jsonToIni as D, type EnvDiffError as E, jsonToToml as F, jsonToXml as G, minifyNdjson as H, type IniSection as I, minifyToml as J, minifyXml as K, ndjsonToJsonArray as L, parseCsv as M, type NdjsonLineResult as N, parseEnv as O, type ParseOptions as P, parseIni as Q, parseSubtitleTimestamp as R, type SubtitleCue as S, type TomlFormatResult as T, parseSubtitles as U, type ValidationResult as V, parseXmlToObject as W, type XmlFormatResult as X, serializeSubtitles as Y, shiftSubtitles as Z, stringifyIni as _, type CsvParseResult as a, tomlToJson as a0, validateEnvKey as a1, validateIni as a2, validateNdjson as a3, validateToml as a4, validateXml as a5, validateYaml as a6, xmlToJson as a7, type EnvDiffResult as b, type EnvEntry as c, data as d, type EnvParseResult as e, type NdjsonValidateResult as f, type ParsedIni as g, type ParsedSubtitles as h, type SubtitleFormat as i, type XmlToJsonOptions as j, convertSubtitles as k, csvToJson as l, detectDelimiter as m, detectSubtitleFormat as n, diffEnvExample as o, envToExport as p, envToJson as q, formatNdjson as r, formatSubtitleTimestamp as s, formatToml as t, formatXml as u, getSection as v, getSectionNames as w, getTomlStats as x, getXmlStats as y, getYamlStats as z };
|
package/dist/index.cjs
CHANGED
|
@@ -4467,12 +4467,15 @@ function detectFillerWords(text) {
|
|
|
4467
4467
|
// src/tools/data.ts
|
|
4468
4468
|
var data_exports = {};
|
|
4469
4469
|
__export(data_exports, {
|
|
4470
|
+
convertSubtitles: () => convertSubtitles,
|
|
4470
4471
|
csvToJson: () => csvToJson2,
|
|
4471
4472
|
detectDelimiter: () => detectDelimiter2,
|
|
4473
|
+
detectSubtitleFormat: () => detectSubtitleFormat,
|
|
4472
4474
|
diffEnvExample: () => diffEnvExample,
|
|
4473
4475
|
envToExport: () => envToExport,
|
|
4474
4476
|
envToJson: () => envToJson,
|
|
4475
4477
|
formatNdjson: () => formatNdjson,
|
|
4478
|
+
formatSubtitleTimestamp: () => formatSubtitleTimestamp,
|
|
4476
4479
|
formatToml: () => formatToml,
|
|
4477
4480
|
formatXml: () => formatXml,
|
|
4478
4481
|
getSection: () => getSection,
|
|
@@ -4492,7 +4495,11 @@ __export(data_exports, {
|
|
|
4492
4495
|
parseCsv: () => parseCsv,
|
|
4493
4496
|
parseEnv: () => parseEnv,
|
|
4494
4497
|
parseIni: () => parseIni,
|
|
4498
|
+
parseSubtitleTimestamp: () => parseSubtitleTimestamp,
|
|
4499
|
+
parseSubtitles: () => parseSubtitles,
|
|
4495
4500
|
parseXmlToObject: () => parseXmlToObject,
|
|
4501
|
+
serializeSubtitles: () => serializeSubtitles,
|
|
4502
|
+
shiftSubtitles: () => shiftSubtitles,
|
|
4496
4503
|
stringifyIni: () => stringifyIni,
|
|
4497
4504
|
toCsvString: () => toCsvString,
|
|
4498
4505
|
tomlToJson: () => tomlToJson,
|
|
@@ -5619,6 +5626,176 @@ function jsonArrayToNdjson(input) {
|
|
|
5619
5626
|
if (!Array.isArray(parsed)) return { error: "Input must be a JSON array" };
|
|
5620
5627
|
return parsed.map((item) => JSON.stringify(item)).join("\n");
|
|
5621
5628
|
}
|
|
5629
|
+
var _SUBTITLE_TIMESTAMP_RE = /(\d+):(\d{1,2}):(\d{1,2})[.,](\d{1,3})|(\d{1,3}):(\d{1,2})[.,](\d{1,3})/;
|
|
5630
|
+
var _VTT_BLOCK_KEYWORDS = ["NOTE", "STYLE", "REGION"];
|
|
5631
|
+
function parseSubtitleTimestamp(raw) {
|
|
5632
|
+
const m = _SUBTITLE_TIMESTAMP_RE.exec(raw.trim());
|
|
5633
|
+
if (!m || m.index !== 0) return null;
|
|
5634
|
+
let hours;
|
|
5635
|
+
let minutes;
|
|
5636
|
+
let seconds;
|
|
5637
|
+
let millis;
|
|
5638
|
+
if (m[1] !== void 0) {
|
|
5639
|
+
hours = Number(m[1]);
|
|
5640
|
+
minutes = Number(m[2]);
|
|
5641
|
+
seconds = Number(m[3]);
|
|
5642
|
+
millis = m[4];
|
|
5643
|
+
} else {
|
|
5644
|
+
hours = 0;
|
|
5645
|
+
minutes = Number(m[5]);
|
|
5646
|
+
seconds = Number(m[6]);
|
|
5647
|
+
millis = m[7];
|
|
5648
|
+
}
|
|
5649
|
+
if (minutes > 59 || seconds > 59) return null;
|
|
5650
|
+
return hours * 3600 + minutes * 60 + seconds + Number(millis.padEnd(3, "0")) / 1e3;
|
|
5651
|
+
}
|
|
5652
|
+
function formatSubtitleTimestamp(totalSeconds, format) {
|
|
5653
|
+
const totalMs = Math.round(Math.max(0, totalSeconds) * 1e3);
|
|
5654
|
+
const ms = totalMs % 1e3;
|
|
5655
|
+
const totalSecs = Math.floor(totalMs / 1e3);
|
|
5656
|
+
const pad = (n, width = 2) => String(n).padStart(width, "0");
|
|
5657
|
+
return `${pad(Math.floor(totalSecs / 3600))}:${pad(Math.floor(totalSecs / 60) % 60)}:${pad(totalSecs % 60)}${format === "srt" ? "," : "."}${pad(ms, 3)}`;
|
|
5658
|
+
}
|
|
5659
|
+
function _stripBom(input) {
|
|
5660
|
+
return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
|
|
5661
|
+
}
|
|
5662
|
+
function detectSubtitleFormat(input) {
|
|
5663
|
+
const text = _stripBom(input).trimStart();
|
|
5664
|
+
if (/^WEBVTT/.test(text)) return "vtt";
|
|
5665
|
+
const arrow = /(\d{1,3}):(\d{1,2})(?::(\d{1,2}))?([.,])(\d{1,3})\s*-->/.exec(text);
|
|
5666
|
+
if (arrow && arrow[4] === ".") return "vtt";
|
|
5667
|
+
return "srt";
|
|
5668
|
+
}
|
|
5669
|
+
function parseSubtitles(input, format) {
|
|
5670
|
+
if (typeof input !== "string" || input.trim().length === 0) {
|
|
5671
|
+
return { error: "Subtitle input is empty" };
|
|
5672
|
+
}
|
|
5673
|
+
const sourceFormat = format ?? detectSubtitleFormat(input);
|
|
5674
|
+
const normalised = _stripBom(input).replace(/\r\n?/g, "\n");
|
|
5675
|
+
const warnings = [];
|
|
5676
|
+
let body = normalised;
|
|
5677
|
+
if (sourceFormat === "vtt") {
|
|
5678
|
+
const headerMatch = /^WEBVTT[^\n]*\n?/.exec(body.trimStart());
|
|
5679
|
+
if (headerMatch) {
|
|
5680
|
+
body = body.trimStart().slice(headerMatch[0].length);
|
|
5681
|
+
} else {
|
|
5682
|
+
warnings.push('WebVTT input is missing its "WEBVTT" header line');
|
|
5683
|
+
}
|
|
5684
|
+
}
|
|
5685
|
+
const blocks = body.split(/\n[ \t]*\n+/).map((b) => b.trim()).filter((b) => b.length > 0);
|
|
5686
|
+
const cues = [];
|
|
5687
|
+
for (const block of blocks) {
|
|
5688
|
+
const lines = block.split("\n");
|
|
5689
|
+
const arrowIdx = lines.findIndex((l) => l.includes("-->"));
|
|
5690
|
+
if (arrowIdx === -1) {
|
|
5691
|
+
const keyword = _VTT_BLOCK_KEYWORDS.find((k) => lines[0] === k || lines[0].startsWith(`${k} `));
|
|
5692
|
+
if (sourceFormat === "vtt" && keyword) {
|
|
5693
|
+
warnings.push(`Dropped a WebVTT ${keyword} block, which has no SRT equivalent`);
|
|
5694
|
+
} else {
|
|
5695
|
+
warnings.push(`Skipped a block with no "-->" timing line: ${JSON.stringify(lines[0].slice(0, 40))}`);
|
|
5696
|
+
}
|
|
5697
|
+
continue;
|
|
5698
|
+
}
|
|
5699
|
+
const timingLine = lines[arrowIdx];
|
|
5700
|
+
const parts = timingLine.split("-->");
|
|
5701
|
+
const start = parseSubtitleTimestamp(parts[0] ?? "");
|
|
5702
|
+
const rightTrimmed = (parts[1] ?? "").trim();
|
|
5703
|
+
const end = parseSubtitleTimestamp(rightTrimmed);
|
|
5704
|
+
if (start === null || end === null) {
|
|
5705
|
+
warnings.push(`Skipped a cue with an unparseable timing line: ${JSON.stringify(timingLine.slice(0, 60))}`);
|
|
5706
|
+
continue;
|
|
5707
|
+
}
|
|
5708
|
+
const endMatch = _SUBTITLE_TIMESTAMP_RE.exec(rightTrimmed);
|
|
5709
|
+
const settings = endMatch ? rightTrimmed.slice(endMatch[0].length).trim() : "";
|
|
5710
|
+
const idLines = lines.slice(0, arrowIdx).filter((l) => l.trim().length > 0);
|
|
5711
|
+
const text = lines.slice(arrowIdx + 1).join("\n").trim();
|
|
5712
|
+
const cue = { index: cues.length + 1, start, end, text };
|
|
5713
|
+
if (sourceFormat === "vtt" && idLines.length > 0) cue.id = idLines[idLines.length - 1].trim();
|
|
5714
|
+
if (settings.length > 0) cue.settings = settings;
|
|
5715
|
+
if (end < start) warnings.push(`Cue ${cue.index} ends before it starts`);
|
|
5716
|
+
if (text.length === 0) warnings.push(`Cue ${cue.index} has no text`);
|
|
5717
|
+
if (cues.length > 0 && start < cues[cues.length - 1].end) {
|
|
5718
|
+
warnings.push(`Cue ${cue.index} starts before cue ${cue.index - 1} ends (overlapping cues)`);
|
|
5719
|
+
}
|
|
5720
|
+
cues.push(cue);
|
|
5721
|
+
}
|
|
5722
|
+
if (cues.length === 0) {
|
|
5723
|
+
return { error: "No subtitle cues found: check that the input is a valid SRT or WebVTT file" };
|
|
5724
|
+
}
|
|
5725
|
+
return {
|
|
5726
|
+
format: sourceFormat,
|
|
5727
|
+
cues,
|
|
5728
|
+
cueCount: cues.length,
|
|
5729
|
+
duration: Math.round(Math.max(...cues.map((c) => c.end)) * 1e3) / 1e3,
|
|
5730
|
+
warnings
|
|
5731
|
+
};
|
|
5732
|
+
}
|
|
5733
|
+
function serializeSubtitles(cues, format) {
|
|
5734
|
+
const blocks = cues.map((cue, i) => {
|
|
5735
|
+
const time = `${formatSubtitleTimestamp(cue.start, format)} --> ${formatSubtitleTimestamp(cue.end, format)}`;
|
|
5736
|
+
if (format === "srt") return `${i + 1}
|
|
5737
|
+
${time}
|
|
5738
|
+
${cue.text}`;
|
|
5739
|
+
const timing = cue.settings ? `${time} ${cue.settings}` : time;
|
|
5740
|
+
return cue.id ? `${cue.id}
|
|
5741
|
+
${timing}
|
|
5742
|
+
${cue.text}` : `${timing}
|
|
5743
|
+
${cue.text}`;
|
|
5744
|
+
});
|
|
5745
|
+
const body = blocks.join("\n\n");
|
|
5746
|
+
return format === "vtt" ? `WEBVTT
|
|
5747
|
+
|
|
5748
|
+
${body}
|
|
5749
|
+
` : `${body}
|
|
5750
|
+
`;
|
|
5751
|
+
}
|
|
5752
|
+
function convertSubtitles(input, target, sourceFormat) {
|
|
5753
|
+
if (target !== "srt" && target !== "vtt") return { error: 'target must be "srt" or "vtt"' };
|
|
5754
|
+
const parsed = parseSubtitles(input, sourceFormat);
|
|
5755
|
+
if ("error" in parsed) return parsed;
|
|
5756
|
+
const warnings = [...parsed.warnings];
|
|
5757
|
+
if (target === "srt") {
|
|
5758
|
+
const droppedSettings = parsed.cues.filter((c) => c.settings).length;
|
|
5759
|
+
const droppedIds = parsed.cues.filter((c) => c.id).length;
|
|
5760
|
+
if (droppedSettings > 0) {
|
|
5761
|
+
warnings.push(`Dropped WebVTT cue settings on ${droppedSettings} cue(s): SRT has no equivalent`);
|
|
5762
|
+
}
|
|
5763
|
+
if (droppedIds > 0) {
|
|
5764
|
+
warnings.push(`Dropped ${droppedIds} WebVTT cue identifier(s): SRT numbers cues sequentially instead`);
|
|
5765
|
+
}
|
|
5766
|
+
}
|
|
5767
|
+
return {
|
|
5768
|
+
format: target,
|
|
5769
|
+
sourceFormat: parsed.format,
|
|
5770
|
+
output: serializeSubtitles(parsed.cues, target),
|
|
5771
|
+
cueCount: parsed.cueCount,
|
|
5772
|
+
duration: parsed.duration,
|
|
5773
|
+
warnings
|
|
5774
|
+
};
|
|
5775
|
+
}
|
|
5776
|
+
function shiftSubtitles(input, offsetSeconds, target) {
|
|
5777
|
+
if (!Number.isFinite(offsetSeconds)) return { error: "offsetSeconds must be a finite number" };
|
|
5778
|
+
const parsed = parseSubtitles(input);
|
|
5779
|
+
if ("error" in parsed) return parsed;
|
|
5780
|
+
const warnings = [...parsed.warnings];
|
|
5781
|
+
let clamped = 0;
|
|
5782
|
+
const shifted = parsed.cues.map((cue) => {
|
|
5783
|
+
const start = cue.start + offsetSeconds;
|
|
5784
|
+
const end = cue.end + offsetSeconds;
|
|
5785
|
+
if (start < 0) clamped += 1;
|
|
5786
|
+
return { ...cue, start: Math.max(0, start), end: Math.max(0, end) };
|
|
5787
|
+
});
|
|
5788
|
+
if (clamped > 0) warnings.push(`${clamped} cue(s) would start before zero and were clamped to 00:00:00`);
|
|
5789
|
+
const outFormat = target ?? parsed.format;
|
|
5790
|
+
return {
|
|
5791
|
+
format: outFormat,
|
|
5792
|
+
sourceFormat: parsed.format,
|
|
5793
|
+
output: serializeSubtitles(shifted, outFormat),
|
|
5794
|
+
cueCount: shifted.length,
|
|
5795
|
+
duration: Math.round(Math.max(...shifted.map((c) => c.end)) * 1e3) / 1e3,
|
|
5796
|
+
warnings
|
|
5797
|
+
};
|
|
5798
|
+
}
|
|
5622
5799
|
|
|
5623
5800
|
// src/tools/generators.ts
|
|
5624
5801
|
var generators_exports = {};
|
|
@@ -6924,12 +7101,15 @@ __export(units_exports, {
|
|
|
6924
7101
|
COMMON_TAX_RATES: () => COMMON_TAX_RATES,
|
|
6925
7102
|
COMMON_TIP_PERCENTAGES: () => COMMON_TIP_PERCENTAGES,
|
|
6926
7103
|
COMMON_VIEWPORTS: () => COMMON_VIEWPORTS,
|
|
7104
|
+
applySuccessiveDiscounts: () => applySuccessiveDiscounts,
|
|
6927
7105
|
autoFormat: () => autoFormat,
|
|
6928
7106
|
bumpVersion: () => bumpVersion,
|
|
6929
7107
|
calcAspectRatio: () => calcAspectRatio,
|
|
6930
7108
|
calcSubnet: () => calcSubnet,
|
|
6931
7109
|
calculateCompoundInterest: () => calculateCompoundInterest,
|
|
7110
|
+
calculateDiscount: () => calculateDiscount,
|
|
6932
7111
|
calculateLoan: () => calculateLoan,
|
|
7112
|
+
calculateMarginMarkup: () => calculateMarginMarkup,
|
|
6933
7113
|
calculatePercentageChange: () => calculatePercentageChange,
|
|
6934
7114
|
calculateSimpleInterest: () => calculateSimpleInterest,
|
|
6935
7115
|
calculateTax: () => calculateTax,
|
|
@@ -6962,6 +7142,8 @@ __export(units_exports, {
|
|
|
6962
7142
|
isValidCidr: () => isValidCidr,
|
|
6963
7143
|
isValidIp: () => isValidIp,
|
|
6964
7144
|
isValidIpv4: () => isValidIpv4,
|
|
7145
|
+
marginToMarkup: () => marginToMarkup,
|
|
7146
|
+
markupToMargin: () => markupToMargin,
|
|
6965
7147
|
octalToPermissions: () => octalToPermissions,
|
|
6966
7148
|
parseFileSize: () => parseFileSize,
|
|
6967
7149
|
parseInput: () => parseInput,
|
|
@@ -8304,6 +8486,155 @@ function convertAprApy(rate, compoundsPerYear, direction) {
|
|
|
8304
8486
|
difference: round6(apy - apr)
|
|
8305
8487
|
};
|
|
8306
8488
|
}
|
|
8489
|
+
var _MARGIN_FIELDS = ["cost", "price", "marginPercent", "markupPercent"];
|
|
8490
|
+
function calculateMarginMarkup(input) {
|
|
8491
|
+
const provided = (v) => typeof v === "number" && Number.isFinite(v);
|
|
8492
|
+
const given = _MARGIN_FIELDS.filter((f) => provided(input[f]));
|
|
8493
|
+
if (given.length !== 2) {
|
|
8494
|
+
return { error: `Provide exactly two of cost, price, marginPercent, markupPercent (got ${given.length})` };
|
|
8495
|
+
}
|
|
8496
|
+
const has = (f) => given.includes(f);
|
|
8497
|
+
const cost0 = input.cost;
|
|
8498
|
+
const price0 = input.price;
|
|
8499
|
+
const margin0 = input.marginPercent;
|
|
8500
|
+
const markup0 = input.markupPercent;
|
|
8501
|
+
if (has("cost") && cost0 <= 0) return { error: "Cost must be greater than zero" };
|
|
8502
|
+
if (has("price") && price0 <= 0) return { error: "Price must be greater than zero" };
|
|
8503
|
+
if (has("marginPercent") && margin0 >= 100) {
|
|
8504
|
+
return { error: "Margin percent must be below 100 (a 100% margin implies a zero cost)" };
|
|
8505
|
+
}
|
|
8506
|
+
if (has("markupPercent") && markup0 <= -100) {
|
|
8507
|
+
return { error: "Markup percent must be greater than -100" };
|
|
8508
|
+
}
|
|
8509
|
+
let cost;
|
|
8510
|
+
let price;
|
|
8511
|
+
if (has("cost") && has("price")) {
|
|
8512
|
+
cost = cost0;
|
|
8513
|
+
price = price0;
|
|
8514
|
+
} else if (has("cost") && has("marginPercent")) {
|
|
8515
|
+
cost = cost0;
|
|
8516
|
+
price = cost0 / (1 - margin0 / 100);
|
|
8517
|
+
} else if (has("cost") && has("markupPercent")) {
|
|
8518
|
+
cost = cost0;
|
|
8519
|
+
price = cost0 * (1 + markup0 / 100);
|
|
8520
|
+
} else if (has("price") && has("marginPercent")) {
|
|
8521
|
+
price = price0;
|
|
8522
|
+
cost = price0 * (1 - margin0 / 100);
|
|
8523
|
+
} else if (has("price") && has("markupPercent")) {
|
|
8524
|
+
price = price0;
|
|
8525
|
+
cost = price0 / (1 + markup0 / 100);
|
|
8526
|
+
} else {
|
|
8527
|
+
return { error: "Margin and markup alone cannot be solved: also provide a cost or a price" };
|
|
8528
|
+
}
|
|
8529
|
+
if (!Number.isFinite(cost) || !Number.isFinite(price)) return { error: "Inputs produce a non-finite result" };
|
|
8530
|
+
if (cost <= 0) return { error: "Those inputs imply a cost of zero or less" };
|
|
8531
|
+
if (price <= 0) return { error: "Those inputs imply a price of zero or less" };
|
|
8532
|
+
const round4 = (n) => Math.round(n * 1e4) / 1e4;
|
|
8533
|
+
const profit = price - cost;
|
|
8534
|
+
return {
|
|
8535
|
+
cost: round4(cost),
|
|
8536
|
+
price: round4(price),
|
|
8537
|
+
profit: round4(profit),
|
|
8538
|
+
marginPercent: round4(profit / price * 100),
|
|
8539
|
+
markupPercent: round4(profit / cost * 100),
|
|
8540
|
+
given,
|
|
8541
|
+
solvedFor: _MARGIN_FIELDS.filter((f) => !given.includes(f))
|
|
8542
|
+
};
|
|
8543
|
+
}
|
|
8544
|
+
function marginToMarkup(marginPercent) {
|
|
8545
|
+
if (!Number.isFinite(marginPercent) || marginPercent >= 100) {
|
|
8546
|
+
return { error: "Margin percent must be a finite number below 100" };
|
|
8547
|
+
}
|
|
8548
|
+
return Math.round(marginPercent / (100 - marginPercent) * 100 * 1e4) / 1e4;
|
|
8549
|
+
}
|
|
8550
|
+
function markupToMargin(markupPercent) {
|
|
8551
|
+
if (!Number.isFinite(markupPercent) || markupPercent <= -100) {
|
|
8552
|
+
return { error: "Markup percent must be a finite number greater than -100" };
|
|
8553
|
+
}
|
|
8554
|
+
return Math.round(markupPercent / (100 + markupPercent) * 100 * 1e4) / 1e4;
|
|
8555
|
+
}
|
|
8556
|
+
var _DISCOUNT_FIELDS = ["discountPercent", "discountAmount", "finalPrice"];
|
|
8557
|
+
function calculateDiscount(input) {
|
|
8558
|
+
const provided = (v) => typeof v === "number" && Number.isFinite(v);
|
|
8559
|
+
if (!provided(input.originalPrice)) {
|
|
8560
|
+
return { error: "originalPrice is required and must be a finite number" };
|
|
8561
|
+
}
|
|
8562
|
+
const originalPrice = input.originalPrice;
|
|
8563
|
+
if (originalPrice <= 0) return { error: "Original price must be greater than zero" };
|
|
8564
|
+
const given = _DISCOUNT_FIELDS.filter((f) => provided(input[f]));
|
|
8565
|
+
if (given.length !== 1) {
|
|
8566
|
+
return { error: `Provide exactly one of discountPercent, discountAmount, finalPrice (got ${given.length})` };
|
|
8567
|
+
}
|
|
8568
|
+
const field = given[0];
|
|
8569
|
+
let discountAmount;
|
|
8570
|
+
if (field === "discountPercent") {
|
|
8571
|
+
const pct = input.discountPercent;
|
|
8572
|
+
if (pct < 0) return { error: "Discount percent cannot be negative" };
|
|
8573
|
+
if (pct > 100) return { error: "Discount percent cannot exceed 100" };
|
|
8574
|
+
discountAmount = originalPrice * (pct / 100);
|
|
8575
|
+
} else if (field === "discountAmount") {
|
|
8576
|
+
const amt = input.discountAmount;
|
|
8577
|
+
if (amt < 0) return { error: "Discount amount cannot be negative" };
|
|
8578
|
+
if (amt > originalPrice) return { error: "Discount amount cannot exceed the original price" };
|
|
8579
|
+
discountAmount = amt;
|
|
8580
|
+
} else {
|
|
8581
|
+
const fin = input.finalPrice;
|
|
8582
|
+
if (fin < 0) return { error: "Final price cannot be negative" };
|
|
8583
|
+
if (fin > originalPrice) {
|
|
8584
|
+
return { error: "Final price cannot exceed the original price: that is a price increase, not a discount" };
|
|
8585
|
+
}
|
|
8586
|
+
discountAmount = originalPrice - fin;
|
|
8587
|
+
}
|
|
8588
|
+
const round4 = (n) => Math.round(n * 1e4) / 1e4;
|
|
8589
|
+
const finalPrice = originalPrice - discountAmount;
|
|
8590
|
+
return {
|
|
8591
|
+
originalPrice: round4(originalPrice),
|
|
8592
|
+
finalPrice: round4(finalPrice),
|
|
8593
|
+
discountAmount: round4(discountAmount),
|
|
8594
|
+
discountPercent: round4(discountAmount / originalPrice * 100),
|
|
8595
|
+
given: field,
|
|
8596
|
+
solvedFor: _DISCOUNT_FIELDS.filter((f) => f !== field)
|
|
8597
|
+
};
|
|
8598
|
+
}
|
|
8599
|
+
function applySuccessiveDiscounts(originalPrice, discountPercents) {
|
|
8600
|
+
if (!Number.isFinite(originalPrice) || originalPrice <= 0) {
|
|
8601
|
+
return { error: "Original price must be a finite number greater than zero" };
|
|
8602
|
+
}
|
|
8603
|
+
if (!Array.isArray(discountPercents) || discountPercents.length === 0) {
|
|
8604
|
+
return { error: "Provide at least one discount percent" };
|
|
8605
|
+
}
|
|
8606
|
+
if (discountPercents.length > 20) {
|
|
8607
|
+
return { error: "Provide at most 20 successive discounts" };
|
|
8608
|
+
}
|
|
8609
|
+
const round4 = (n) => Math.round(n * 1e4) / 1e4;
|
|
8610
|
+
const steps = [];
|
|
8611
|
+
let running = originalPrice;
|
|
8612
|
+
let sumOfDiscountPercents = 0;
|
|
8613
|
+
for (const pct of discountPercents) {
|
|
8614
|
+
if (!Number.isFinite(pct)) return { error: "Every discount percent must be a finite number" };
|
|
8615
|
+
if (pct < 0) return { error: "Discount percent cannot be negative" };
|
|
8616
|
+
if (pct > 100) return { error: "Discount percent cannot exceed 100" };
|
|
8617
|
+
const priceBefore = running;
|
|
8618
|
+
const saved = priceBefore * (pct / 100);
|
|
8619
|
+
running = priceBefore - saved;
|
|
8620
|
+
sumOfDiscountPercents += pct;
|
|
8621
|
+
steps.push({
|
|
8622
|
+
discountPercent: round4(pct),
|
|
8623
|
+
priceBefore: round4(priceBefore),
|
|
8624
|
+
priceAfter: round4(running),
|
|
8625
|
+
saved: round4(saved)
|
|
8626
|
+
});
|
|
8627
|
+
}
|
|
8628
|
+
const totalSaved = originalPrice - running;
|
|
8629
|
+
return {
|
|
8630
|
+
originalPrice: round4(originalPrice),
|
|
8631
|
+
finalPrice: round4(running),
|
|
8632
|
+
totalSaved: round4(totalSaved),
|
|
8633
|
+
effectiveDiscountPercent: round4(totalSaved / originalPrice * 100),
|
|
8634
|
+
sumOfDiscountPercents: round4(sumOfDiscountPercents),
|
|
8635
|
+
steps
|
|
8636
|
+
};
|
|
8637
|
+
}
|
|
8307
8638
|
|
|
8308
8639
|
// src/tools/network.ts
|
|
8309
8640
|
var network_exports = {};
|
package/dist/index.d.cts
CHANGED
|
@@ -2,10 +2,10 @@ export { j as json } from './json-DEVFIpsC.cjs';
|
|
|
2
2
|
export { e as encoding } from './encoding-Dk3wpmb3.cjs';
|
|
3
3
|
export { h as hashing } from './hashing-CnetQFD_.cjs';
|
|
4
4
|
export { t as text } from './text-DS70aMY5.cjs';
|
|
5
|
-
export { d as data } from './data-
|
|
5
|
+
export { d as data } from './data-XRb0D7Zn.cjs';
|
|
6
6
|
export { g as generators } from './generators-BGtRGpJZ.cjs';
|
|
7
7
|
export { t as time } from './time-KqJjdqII.cjs';
|
|
8
|
-
export { u as units } from './units-
|
|
8
|
+
export { u as units } from './units-DAoSM2g8.cjs';
|
|
9
9
|
export { n as network } from './network-BJOjJnmY.cjs';
|
|
10
10
|
export { a as api } from './api-Xl7OzIgq.cjs';
|
|
11
11
|
export { c as code } from './code-DaRvff2v.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -2,10 +2,10 @@ export { j as json } from './json-DEVFIpsC.js';
|
|
|
2
2
|
export { e as encoding } from './encoding-Dk3wpmb3.js';
|
|
3
3
|
export { h as hashing } from './hashing-CnetQFD_.js';
|
|
4
4
|
export { t as text } from './text-DS70aMY5.js';
|
|
5
|
-
export { d as data } from './data-
|
|
5
|
+
export { d as data } from './data-XRb0D7Zn.js';
|
|
6
6
|
export { g as generators } from './generators-BGtRGpJZ.js';
|
|
7
7
|
export { t as time } from './time-KqJjdqII.js';
|
|
8
|
-
export { u as units } from './units-
|
|
8
|
+
export { u as units } from './units-DAoSM2g8.js';
|
|
9
9
|
export { n as network } from './network-BJOjJnmY.js';
|
|
10
10
|
export { a as api } from './api-Xl7OzIgq.js';
|
|
11
11
|
export { c as code } from './code-DaRvff2v.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { media_exports as media } from './chunk-XMKJYVKB.js';
|
|
2
|
-
export { units_exports as units } from './chunk-
|
|
2
|
+
export { units_exports as units } from './chunk-HZIS2DMJ.js';
|
|
3
3
|
export { network_exports as network } from './chunk-Y5OOP6WN.js';
|
|
4
4
|
export { api_exports as api } from './chunk-Q3B3YWOA.js';
|
|
5
5
|
export { code_exports as code } from './chunk-LXKIPPDG.js';
|
|
@@ -11,7 +11,7 @@ export { json_exports as json } from './chunk-WO3LJMVV.js';
|
|
|
11
11
|
export { encoding_exports as encoding } from './chunk-DOWQZUTP.js';
|
|
12
12
|
export { hashing_exports as hashing } from './chunk-GEFVMGZR.js';
|
|
13
13
|
export { text_exports as text } from './chunk-2I4O5YCT.js';
|
|
14
|
-
export { data_exports as data } from './chunk-
|
|
14
|
+
export { data_exports as data } from './chunk-66QHV5KW.js';
|
|
15
15
|
export { generators_exports as generators } from './chunk-2CJSLYWI.js';
|
|
16
16
|
export { time_exports as time } from './chunk-IEPWZERQ.js';
|
|
17
17
|
import './chunk-MLKGABMK.js';
|