kopia lustrzana https://github.com/mifi/lossless-cut
upgrade execa and node types
rodzic
2fb78214fc
commit
ca8eccf899
|
@ -45,6 +45,7 @@
|
||||||
"@radix-ui/react-checkbox": "^1.0.4",
|
"@radix-ui/react-checkbox": "^1.0.4",
|
||||||
"@radix-ui/react-switch": "^1.0.1",
|
"@radix-ui/react-switch": "^1.0.1",
|
||||||
"@tsconfig/node18": "^18.2.2",
|
"@tsconfig/node18": "^18.2.2",
|
||||||
|
"@tsconfig/node20": "^20.1.4",
|
||||||
"@tsconfig/strictest": "^2.0.2",
|
"@tsconfig/strictest": "^2.0.2",
|
||||||
"@tsconfig/vite-react": "^3.0.0",
|
"@tsconfig/vite-react": "^3.0.0",
|
||||||
"@types/color": "^3.0.6",
|
"@types/color": "^3.0.6",
|
||||||
|
@ -58,7 +59,7 @@
|
||||||
"@types/mime-types": "^2.1.4",
|
"@types/mime-types": "^2.1.4",
|
||||||
"@types/morgan": "^1.9.9",
|
"@types/morgan": "^1.9.9",
|
||||||
"@types/mousetrap": "^1.6.15",
|
"@types/mousetrap": "^1.6.15",
|
||||||
"@types/node": "18",
|
"@types/node": "20",
|
||||||
"@types/react": "^18.2.66",
|
"@types/react": "^18.2.66",
|
||||||
"@types/react-dom": "^18.2.22",
|
"@types/react-dom": "^18.2.22",
|
||||||
"@types/sortablejs": "^1.15.0",
|
"@types/sortablejs": "^1.15.0",
|
||||||
|
@ -122,7 +123,7 @@
|
||||||
"tiny-invariant": "^1.3.3",
|
"tiny-invariant": "^1.3.3",
|
||||||
"tsx": "^4.7.1",
|
"tsx": "^4.7.1",
|
||||||
"type-fest": "^4.23.0",
|
"type-fest": "^4.23.0",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.7.2",
|
||||||
"use-debounce": "^5.1.0",
|
"use-debounce": "^5.1.0",
|
||||||
"use-trace-update": "^1.3.0",
|
"use-trace-update": "^1.3.0",
|
||||||
"vite": "^5.3.6",
|
"vite": "^5.3.6",
|
||||||
|
@ -134,7 +135,7 @@
|
||||||
"cue-parser": "^0.3.0",
|
"cue-parser": "^0.3.0",
|
||||||
"electron-store": "5.1.1",
|
"electron-store": "5.1.1",
|
||||||
"electron-unhandled": "^5.0.0",
|
"electron-unhandled": "^5.0.0",
|
||||||
"execa": "^8.0.1",
|
"execa": "^9.5.1",
|
||||||
"express": "^4.20.0",
|
"express": "^4.20.0",
|
||||||
"express-async-handler": "^1.2.0",
|
"express-async-handler": "^1.2.0",
|
||||||
"file-type": "patch:file-type@npm%3A19.4.1#~/.yarn/patches/file-type-npm-19.4.1-d18086444c.patch",
|
"file-type": "patch:file-type@npm%3A19.4.1#~/.yarn/patches/file-type-npm-19.4.1-d18086444c.patch",
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { DateTime } from 'luxon';
|
||||||
const xmlUrl = new URL('../no.mifi.losslesscut.appdata.xml', import.meta.url);
|
const xmlUrl = new URL('../no.mifi.losslesscut.appdata.xml', import.meta.url);
|
||||||
const xmlData = await readFile(xmlUrl);
|
const xmlData = await readFile(xmlUrl);
|
||||||
|
|
||||||
const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url)) as unknown as string);
|
const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
|
||||||
|
|
||||||
const parser = new XMLParser({ alwaysCreateTextNode: true, ignoreAttributes: false, ignoreDeclaration: false });
|
const parser = new XMLParser({ alwaysCreateTextNode: true, ignoreAttributes: false, ignoreDeclaration: false });
|
||||||
const xml = parser.parse(xmlData);
|
const xml = parser.parse(xmlData);
|
||||||
|
|
|
@ -18,7 +18,7 @@ const bundleId = args[4];
|
||||||
// seems to be the same
|
// seems to be the same
|
||||||
const ascPublicId = apiIssuer;
|
const ascPublicId = apiIssuer;
|
||||||
|
|
||||||
const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url)) as unknown as string);
|
const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
|
||||||
|
|
||||||
console.log('Using version', packageJson.version);
|
console.log('Using version', packageJson.version);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import assert from 'node:assert';
|
import assert from 'node:assert';
|
||||||
|
import { ExecaError } from 'execa';
|
||||||
|
|
||||||
import logger from './logger.js';
|
import logger from './logger.js';
|
||||||
import { createMediaSourceProcess, readOneJpegFrame as readOneJpegFrameRaw } from './ffmpeg.js';
|
import { createMediaSourceProcess, readOneJpegFrame as readOneJpegFrameRaw } from './ffmpeg.js';
|
||||||
|
@ -64,16 +65,12 @@ export function createMediaSourceStream({ path, videoStreamIndex, audioStreamInd
|
||||||
try {
|
try {
|
||||||
await process;
|
await process;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof Error && err.name === 'AbortError') {
|
if (err instanceof ExecaError && err.isTerminated) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error todo
|
logger.warn((err as Error).message);
|
||||||
if (!(err.killed)) {
|
logger.warn(stderr.toString('utf8'));
|
||||||
// @ts-expect-error todo
|
|
||||||
logger.warn(err.message);
|
|
||||||
logger.warn(stderr.toString('utf8'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
import readline from 'node:readline';
|
import readline from 'node:readline';
|
||||||
import stringToStream from 'string-to-stream';
|
import stringToStream from 'string-to-stream';
|
||||||
import { BufferEncodingOption, execa, ExecaChildProcess, Options as ExecaOptions } from 'execa';
|
import { execa, Options as ExecaOptions, ResultPromise } from 'execa';
|
||||||
import assert from 'node:assert';
|
import assert from 'node:assert';
|
||||||
import { Readable } from 'node:stream';
|
import { Readable } from 'node:stream';
|
||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
|
@ -14,7 +14,7 @@ import logger from './logger.js';
|
||||||
import { parseFfmpegProgressLine } from './progress.js';
|
import { parseFfmpegProgressLine } from './progress.js';
|
||||||
|
|
||||||
|
|
||||||
const runningFfmpegs = new Set<ExecaChildProcess<Buffer>>();
|
const runningFfmpegs = new Set<ResultPromise<Omit<ExecaOptions, 'encoding'> & { encoding: 'buffer' }>>();
|
||||||
// setInterval(() => console.log(runningFfmpegs.size), 1000);
|
// setInterval(() => console.log(runningFfmpegs.size), 1000);
|
||||||
|
|
||||||
let customFfPath: string | undefined;
|
let customFfPath: string | undefined;
|
||||||
|
@ -59,7 +59,7 @@ export const getFfmpegPath = () => getFfPath('ffmpeg');
|
||||||
export function abortFfmpegs() {
|
export function abortFfmpegs() {
|
||||||
logger.info('Aborting', runningFfmpegs.size, 'ffmpeg process(es)');
|
logger.info('Aborting', runningFfmpegs.size, 'ffmpeg process(es)');
|
||||||
runningFfmpegs.forEach((process) => {
|
runningFfmpegs.forEach((process) => {
|
||||||
process.kill('SIGTERM', { forceKillAfterTimeout: 10000 });
|
process.kill();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,20 +88,27 @@ function handleProgress(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getExecaOptions({ env, ...customExecaOptions }: Omit<ExecaOptions<BufferEncodingOption>, 'buffer'> = {}) {
|
function getExecaOptions({ env, cancelSignal, ...rest }: ExecaOptions = {}) {
|
||||||
const execaOptions: Omit<ExecaOptions<BufferEncodingOption>, 'buffer'> = { ...customExecaOptions, encoding: 'buffer' };
|
// This is a ugly hack to please execa which expects cancelSignal to be a prototype of AbortSignal
|
||||||
// https://github.com/mifi/lossless-cut/issues/1143#issuecomment-1500883489
|
// however this gets lost during @electron/remote passing
|
||||||
if (isLinux && !isDev && !customFfPath) {
|
// https://github.com/sindresorhus/execa/blob/c8cff27a47b6e6f1cfbfec2bf7fa9dcd08cefed1/lib/terminate/cancel.js#L5
|
||||||
return {
|
if (cancelSignal != null) Object.setPrototypeOf(cancelSignal, new AbortController().signal);
|
||||||
...execaOptions,
|
|
||||||
env: { ...env, LD_LIBRARY_PATH: process.resourcesPath },
|
const execaOptions: Pick<ExecaOptions, 'env'> & { encoding: 'buffer' } = {
|
||||||
};
|
...(cancelSignal != null && { cancelSignal }),
|
||||||
}
|
...rest,
|
||||||
|
encoding: 'buffer' as const,
|
||||||
|
env: {
|
||||||
|
...env,
|
||||||
|
// https://github.com/mifi/lossless-cut/issues/1143#issuecomment-1500883489
|
||||||
|
...(isLinux && !isDev && !customFfPath && { LD_LIBRARY_PATH: process.resourcesPath }),
|
||||||
|
},
|
||||||
|
};
|
||||||
return execaOptions;
|
return execaOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo collect warnings from ffmpeg output and show them after export? example: https://github.com/mifi/lossless-cut/issues/1469
|
// todo collect warnings from ffmpeg output and show them after export? example: https://github.com/mifi/lossless-cut/issues/1469
|
||||||
function runFfmpegProcess(args: readonly string[], customExecaOptions?: Omit<ExecaOptions<BufferEncodingOption>, 'encoding'>, additionalOptions?: { logCli?: boolean }) {
|
function runFfmpegProcess(args: readonly string[], customExecaOptions?: ExecaOptions, additionalOptions?: { logCli?: boolean }) {
|
||||||
const ffmpegPath = getFfmpegPath();
|
const ffmpegPath = getFfmpegPath();
|
||||||
const { logCli = true } = additionalOptions ?? {};
|
const { logCli = true } = additionalOptions ?? {};
|
||||||
if (logCli) logger.info(getFfCommandLine('ffmpeg', args));
|
if (logCli) logger.info(getFfCommandLine('ffmpeg', args));
|
||||||
|
@ -187,8 +194,8 @@ export async function renderWaveformPng({ filePath, start, duration, color, stre
|
||||||
|
|
||||||
logger.info(`${getFfCommandLine('ffmpeg1', args1)} | \n${getFfCommandLine('ffmpeg2', args2)}`);
|
logger.info(`${getFfCommandLine('ffmpeg1', args1)} | \n${getFfCommandLine('ffmpeg2', args2)}`);
|
||||||
|
|
||||||
let ps1: ExecaChildProcess<Buffer> | undefined;
|
let ps1: ResultPromise<{ encoding: 'buffer' }> | undefined;
|
||||||
let ps2: ExecaChildProcess<Buffer> | undefined;
|
let ps2: ResultPromise<{ encoding: 'buffer' }> | undefined;
|
||||||
try {
|
try {
|
||||||
ps1 = runFfmpegProcess(args1, { buffer: false }, { logCli: false });
|
ps1 = runFfmpegProcess(args1, { buffer: false }, { logCli: false });
|
||||||
ps2 = runFfmpegProcess(args2, undefined, { logCli: false });
|
ps2 = runFfmpegProcess(args2, undefined, { logCli: false });
|
||||||
|
@ -210,7 +217,7 @@ export async function renderWaveformPng({ filePath, start, duration, color, stre
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
buffer: stdout,
|
buffer: Buffer.from(stdout),
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (ps1) ps1.kill();
|
if (ps1) ps1.kill();
|
||||||
|
@ -465,7 +472,7 @@ async function readFormatData(filePath: string) {
|
||||||
const { stdout } = await runFfprobe([
|
const { stdout } = await runFfprobe([
|
||||||
'-of', 'json', '-show_format', '-i', filePath, '-hide_banner',
|
'-of', 'json', '-show_format', '-i', filePath, '-hide_banner',
|
||||||
]);
|
]);
|
||||||
return JSON.parse(stdout as unknown as string).format;
|
return JSON.parse(new TextDecoder().decode(stdout)).format;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getDuration(filePath: string) {
|
export async function getDuration(filePath: string) {
|
||||||
|
@ -557,7 +564,7 @@ export function createMediaSourceProcess({ path, videoStreamIndex, audioStreamIn
|
||||||
|
|
||||||
if (enableLog) logger.info(getFfCommandLine('ffmpeg', args));
|
if (enableLog) logger.info(getFfCommandLine('ffmpeg', args));
|
||||||
|
|
||||||
return execa(getFfmpegPath(), args, { encoding: null, buffer: false, stderr: enableLog ? 'inherit' : 'pipe' });
|
return execa(getFfmpegPath(), args, { encoding: 'buffer', buffer: false, stderr: enableLog ? 'inherit' : 'pipe' });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function downloadMediaUrl(url: string, outPath: string) {
|
export async function downloadMediaUrl(url: string, outPath: string) {
|
||||||
|
|
|
@ -230,7 +230,7 @@ async function startPlayback({ path, video, videoStreamIndex, audioStreamIndex,
|
||||||
processChunk();
|
processChunk();
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawJpegFrame(canvas: HTMLCanvasElement | null, jpegImage: Buffer) {
|
function drawJpegFrame(canvas: HTMLCanvasElement | null, jpegImage: Uint8Array) {
|
||||||
if (!canvas) return;
|
if (!canvas) return;
|
||||||
|
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
|
@ -244,7 +244,8 @@ function drawJpegFrame(canvas: HTMLCanvasElement | null, jpegImage: Buffer) {
|
||||||
img.onload = () => ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
img.onload = () => ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||||
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
||||||
img.onerror = (error) => console.error('Canvas JPEG image error', error);
|
img.onerror = (error) => console.error('Canvas JPEG image error', error);
|
||||||
img.src = `data:image/jpeg;base64,${jpegImage.toString('base64')}`;
|
// todo use Blob?
|
||||||
|
img.src = `data:image/jpeg;base64,${Buffer.from(jpegImage).toString('base64')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createPauseImage({ path, seekTo, videoStreamIndex, canvas, signal }: {
|
async function createPauseImage({ path, seekTo, videoStreamIndex, canvas, signal }: {
|
||||||
|
|
|
@ -84,7 +84,7 @@ export async function saveLlcProject({ savePath, filePath, cutSegments }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadLlcProject(path: string) {
|
export async function loadLlcProject(path: string) {
|
||||||
const parsed = JSON5.parse(await readFile(path) as unknown as string) as unknown;
|
const parsed = JSON5.parse(await readFile(path, 'utf8')) as unknown;
|
||||||
if (parsed == null || typeof parsed !== 'object') throw new Error('Invalid LLC file');
|
if (parsed == null || typeof parsed !== 'object') throw new Error('Invalid LLC file');
|
||||||
let mediaFileName: string | undefined;
|
let mediaFileName: string | undefined;
|
||||||
if ('mediaFileName' in parsed && typeof parsed.mediaFileName === 'string') {
|
if ('mediaFileName' in parsed && typeof parsed.mediaFileName === 'string') {
|
||||||
|
|
|
@ -27,23 +27,23 @@ export class RefuseOverwriteError extends Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fixRemoteBuffer(buffer: Buffer) {
|
export function safeCreateBlob(array: Uint8Array, options?: BlobPropertyBag) {
|
||||||
// if we don't do this when creating a Blob, we get:
|
// if we don't do this when creating a Blob, we get:
|
||||||
// "Failed to construct 'Blob': The provided ArrayBufferView value must not be resizable."
|
// "Failed to construct 'Blob': The provided ArrayBufferView value must not be resizable."
|
||||||
// maybe when moving away from @electron/remote, it's not needed anymore?
|
// maybe when moving away from @electron/remote, it's not needed anymore?
|
||||||
const buffer2 = Buffer.allocUnsafe(buffer.length);
|
// https://stackoverflow.com/a/25255750/6519037
|
||||||
buffer.copy(buffer2);
|
const cloned = new Uint8Array(array);
|
||||||
return buffer2;
|
return new Blob([cloned], options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logStdoutStderr({ stdout, stderr }: { stdout: Buffer, stderr: Buffer }) {
|
export function logStdoutStderr({ stdout, stderr }: { stdout: Uint8Array, stderr: Uint8Array }) {
|
||||||
if (stdout.length > 0) {
|
if (stdout.length > 0) {
|
||||||
console.log('%cSTDOUT:', 'color: green; font-weight: bold');
|
console.log('%cSTDOUT:', 'color: green; font-weight: bold');
|
||||||
console.log(stdout.toString('utf8'));
|
console.log(new TextDecoder().decode(stdout));
|
||||||
}
|
}
|
||||||
if (stderr.length > 0) {
|
if (stderr.length > 0) {
|
||||||
console.log('%cSTDERR:', 'color: blue; font-weight: bold');
|
console.log('%cSTDERR:', 'color: blue; font-weight: bold');
|
||||||
console.log(stderr.toString('utf8'));
|
console.log(new TextDecoder().decode(stderr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ export async function readFrames({ filePath, from, to, streamIndex }: {
|
||||||
}) {
|
}) {
|
||||||
const intervalsArgs = from != null && to != null ? ['-read_intervals', `${from}%${to}`] : [];
|
const intervalsArgs = from != null && to != null ? ['-read_intervals', `${from}%${to}`] : [];
|
||||||
const { stdout } = await runFfprobe(['-v', 'error', ...intervalsArgs, '-show_packets', '-select_streams', String(streamIndex), '-show_entries', 'packet=pts_time,flags', '-of', 'json', filePath], { logCli: false });
|
const { stdout } = await runFfprobe(['-v', 'error', ...intervalsArgs, '-show_packets', '-select_streams', String(streamIndex), '-show_entries', 'packet=pts_time,flags', '-of', 'json', filePath], { logCli: false });
|
||||||
const packetsFiltered: Frame[] = (JSON.parse(stdout as unknown as string).packets as { flags: string, pts_time: string }[])
|
const packetsFiltered: Frame[] = (JSON.parse(new TextDecoder().decode(stdout)).packets as { flags: string, pts_time: string }[])
|
||||||
.map((p) => ({
|
.map((p) => ({
|
||||||
keyframe: p.flags[0] === 'K',
|
keyframe: p.flags[0] === 'K',
|
||||||
time: parseFloat(p.pts_time),
|
time: parseFloat(p.pts_time),
|
||||||
|
@ -356,7 +356,7 @@ export async function readFileMeta(filePath: string) {
|
||||||
let parsedJson: FFprobeProbeResult;
|
let parsedJson: FFprobeProbeResult;
|
||||||
try {
|
try {
|
||||||
// https://github.com/mifi/lossless-cut/issues/1342
|
// https://github.com/mifi/lossless-cut/issues/1342
|
||||||
parsedJson = JSON.parse(stdout.toString('utf8'));
|
parsedJson = JSON.parse(new TextDecoder().decode(stdout));
|
||||||
} catch {
|
} catch {
|
||||||
console.log('ffprobe stdout', stdout);
|
console.log('ffprobe stdout', stdout);
|
||||||
throw new Error('ffprobe returned malformed data');
|
throw new Error('ffprobe returned malformed data');
|
||||||
|
@ -383,9 +383,9 @@ async function renderThumbnail(filePath: string, timestamp: number, signal: Abor
|
||||||
'-',
|
'-',
|
||||||
];
|
];
|
||||||
|
|
||||||
const { stdout } = await runFfmpeg(args, { signal }, { logCli: false });
|
const { stdout } = await runFfmpeg(args, { cancelSignal: signal }, { logCli: false });
|
||||||
|
|
||||||
const blob = new Blob([fixRemoteBuffer(stdout)], { type: 'image/jpeg' });
|
const blob = safeCreateBlob(stdout, { type: 'image/jpeg' });
|
||||||
return URL.createObjectURL(blob);
|
return URL.createObjectURL(blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,7 +399,7 @@ export async function extractSubtitleTrack(filePath: string, streamId: number) {
|
||||||
];
|
];
|
||||||
|
|
||||||
const { stdout } = await runFfmpeg(args);
|
const { stdout } = await runFfmpeg(args);
|
||||||
return stdout.toString('utf8');
|
return new TextDecoder().decode(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function extractSubtitleTrackToSegments(filePath: string, streamId: number) {
|
export async function extractSubtitleTrackToSegments(filePath: string, streamId: number) {
|
||||||
|
@ -423,7 +423,7 @@ export async function extractSubtitleTrackVtt(filePath: string, streamId: number
|
||||||
|
|
||||||
const { stdout } = await runFfmpeg(args);
|
const { stdout } = await runFfmpeg(args);
|
||||||
|
|
||||||
const blob = new Blob([fixRemoteBuffer(stdout)], { type: 'text/vtt' });
|
const blob = safeCreateBlob(stdout, { type: 'text/vtt' });
|
||||||
return URL.createObjectURL(blob);
|
return URL.createObjectURL(blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -762,7 +762,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
|
||||||
appendFfmpegCommandLog(ffmpegArgs);
|
appendFfmpegCommandLog(ffmpegArgs);
|
||||||
const { stdout } = await runFfmpegWithProgress({ ffmpegArgs, duration, onProgress });
|
const { stdout } = await runFfmpegWithProgress({ ffmpegArgs, duration, onProgress });
|
||||||
|
|
||||||
console.log(stdout.toString('utf8'));
|
console.log(new TextDecoder().decode(stdout));
|
||||||
|
|
||||||
invariant(outPath != null);
|
invariant(outPath != null);
|
||||||
await transferTimestamps({ inPath: filePathArg, outPath, treatOutputFileModifiedTimeAsStart });
|
await transferTimestamps({ inPath: filePathArg, outPath, treatOutputFileModifiedTimeAsStart });
|
||||||
|
@ -901,7 +901,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
|
||||||
|
|
||||||
appendFfmpegCommandLog(ffmpegArgs);
|
appendFfmpegCommandLog(ffmpegArgs);
|
||||||
const { stdout } = await runFfmpeg(ffmpegArgs);
|
const { stdout } = await runFfmpeg(ffmpegArgs);
|
||||||
console.log(stdout.toString('utf8'));
|
console.log(new TextDecoder().decode(stdout));
|
||||||
|
|
||||||
return outPaths;
|
return outPaths;
|
||||||
}, [appendFfmpegCommandLog, enableOverwriteOutput, filePath]);
|
}, [appendFfmpegCommandLog, enableOverwriteOutput, filePath]);
|
||||||
|
@ -939,7 +939,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
|
||||||
try {
|
try {
|
||||||
appendFfmpegCommandLog(ffmpegArgs);
|
appendFfmpegCommandLog(ffmpegArgs);
|
||||||
const { stdout } = await runFfmpeg(ffmpegArgs);
|
const { stdout } = await runFfmpeg(ffmpegArgs);
|
||||||
console.log(stdout.toString('utf8'));
|
console.log(new TextDecoder().decode(stdout));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Unfortunately ffmpeg will exit with code 1 even though it's a success
|
// Unfortunately ffmpeg will exit with code 1 even though it's a success
|
||||||
// Note: This is kind of hacky:
|
// Note: This is kind of hacky:
|
||||||
|
|
|
@ -6,6 +6,7 @@ import sortBy from 'lodash/sortBy';
|
||||||
import { renderThumbnails as ffmpegRenderThumbnails } from '../ffmpeg';
|
import { renderThumbnails as ffmpegRenderThumbnails } from '../ffmpeg';
|
||||||
import { Thumbnail } from '../types';
|
import { Thumbnail } from '../types';
|
||||||
import { isDurationValid } from '../segments';
|
import { isDurationValid } from '../segments';
|
||||||
|
import { isExecaError } from '../util';
|
||||||
|
|
||||||
|
|
||||||
export default ({ filePath, zoomedDuration, zoomWindowStartTime, showThumbnails }: {
|
export default ({ filePath, zoomedDuration, zoomWindowStartTime, showThumbnails }: {
|
||||||
|
@ -39,7 +40,7 @@ export default ({ filePath, zoomedDuration, zoomWindowStartTime, showThumbnails
|
||||||
|
|
||||||
await ffmpegRenderThumbnails({ signal: abortController.signal, filePath: debounced.filePath, from: debounced.zoomWindowStartTime, duration: debounced.zoomedDuration, onThumbnail: addThumbnail });
|
await ffmpegRenderThumbnails({ signal: abortController.signal, filePath: debounced.filePath, from: debounced.zoomWindowStartTime, duration: debounced.zoomedDuration, onThumbnail: addThumbnail });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if ((err as Error).name !== 'AbortError') {
|
if ((err as Error).name !== 'AbortError' && !(isExecaError(err) && err.isCanceled)) {
|
||||||
console.error('Failed to render thumbnails', err);
|
console.error('Failed to render thumbnails', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import sortBy from 'lodash/sortBy';
|
||||||
import { useThrottle } from '@uidotdev/usehooks';
|
import { useThrottle } from '@uidotdev/usehooks';
|
||||||
import { waveformColorDark, waveformColorLight } from '../colors';
|
import { waveformColorDark, waveformColorLight } from '../colors';
|
||||||
|
|
||||||
import { fixRemoteBuffer, renderWaveformPng } from '../ffmpeg';
|
import { renderWaveformPng, safeCreateBlob } from '../ffmpeg';
|
||||||
import { RenderableWaveform } from '../types';
|
import { RenderableWaveform } from '../types';
|
||||||
import { FFprobeStream } from '../../../../ffprobe';
|
import { FFprobeStream } from '../../../../ffprobe';
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ export default ({ darkMode, filePath, relevantTime, duration, waveformEnabled, a
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...w,
|
...w,
|
||||||
url: URL.createObjectURL(new Blob([fixRemoteBuffer(buffer)], { type: 'image/png' })),
|
url: URL.createObjectURL(safeCreateBlob(buffer, { type: 'image/png' })),
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import i18n from 'i18next';
|
||||||
import { Trans } from 'react-i18next';
|
import { Trans } from 'react-i18next';
|
||||||
|
|
||||||
import CopyClipboardButton from './components/CopyClipboardButton';
|
import CopyClipboardButton from './components/CopyClipboardButton';
|
||||||
import { isStoreBuild, isMasBuild, isWindowsStoreBuild } from './util';
|
import { isStoreBuild, isMasBuild, isWindowsStoreBuild, isExecaError } from './util';
|
||||||
import { ReactSwal } from './swal';
|
import { ReactSwal } from './swal';
|
||||||
|
|
||||||
const electron = window.require('electron');
|
const electron = window.require('electron');
|
||||||
|
@ -33,15 +33,15 @@ export function openSendReportDialog(err: unknown | undefined, state?: unknown)
|
||||||
const version = app.getVersion();
|
const version = app.getVersion();
|
||||||
|
|
||||||
const text = `${err instanceof Error ? err.stack : 'No error occurred.'}\n\n${JSON.stringify({
|
const text = `${err instanceof Error ? err.stack : 'No error occurred.'}\n\n${JSON.stringify({
|
||||||
err: err instanceof Error && {
|
err: isExecaError(err) && {
|
||||||
code: err['code'],
|
code: err.code,
|
||||||
killed: err['killed'],
|
isTerminated: err.isTerminated,
|
||||||
failed: err['failed'],
|
failed: err.failed,
|
||||||
timedOut: err['timedOut'],
|
timedOut: err.timedOut,
|
||||||
isCanceled: err['isCanceled'],
|
isCanceled: err.isCanceled,
|
||||||
exitCode: err['exitCode'],
|
exitCode: err.exitCode,
|
||||||
signal: err['signal'],
|
signal: err.signal,
|
||||||
signalDescription: err['signalDescription'],
|
signalDescription: err.signalDescription,
|
||||||
},
|
},
|
||||||
|
|
||||||
state,
|
state,
|
||||||
|
|
|
@ -291,17 +291,23 @@ export const deleteDispositionValue = 'llc_disposition_remove';
|
||||||
|
|
||||||
export const mirrorTransform = 'matrix(-1, 0, 0, 1, 0, 0)';
|
export const mirrorTransform = 'matrix(-1, 0, 0, 1, 0, 0)';
|
||||||
|
|
||||||
export type InvariantExecaError = ExecaError<string> | ExecaError<Buffer> | ExecaError<undefined>;
|
// todo this is not a correct assumption
|
||||||
|
export type InvariantExecaError = ExecaError<{ encoding: 'utf8' }> | ExecaError<{ encoding: 'buffer' }>;
|
||||||
|
|
||||||
// note: I don't think we can use instanceof ExecaError because the error has been sent over the main-renderer bridge
|
// We can't use `instanceof ExecaError` because the error has been sent over the main-renderer bridge (@electron/remote)
|
||||||
|
// so instead we just check if it has some of execa's specific error properties
|
||||||
export function isExecaError(err: unknown): err is InvariantExecaError {
|
export function isExecaError(err: unknown): err is InvariantExecaError {
|
||||||
return err instanceof Error && 'stdout' in err && 'stderr' in err;
|
// https://github.com/sindresorhus/execa/blob/main/docs/api.md#resultfailed
|
||||||
|
return err instanceof Error && ('failed' in err && 'shortMessage' in err && 'isForcefullyTerminated' in err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// execa killed (aborted by user)
|
export const isAbortedError = (err: unknown) => (
|
||||||
export const isAbortedError = (err: unknown) => isExecaError(err) && err.killed;
|
// execa killed (aborted by user). isTerminated because runningFfmpegs process.kill
|
||||||
|
(isExecaError(err) && (err.isCanceled || err.isTerminated))
|
||||||
|
|| (err instanceof Error && err.name === 'AbortError')
|
||||||
|
);
|
||||||
|
|
||||||
export const getStdioString = (stdio: string | Buffer | undefined) => (stdio instanceof Buffer ? stdio.toString('utf8') : stdio);
|
export const getStdioString = (stdio: string | Uint8Array) => (stdio instanceof Uint8Array ? Buffer.from(stdio).toString('utf8') : stdio);
|
||||||
|
|
||||||
// A bit hacky but it works, unless someone has a file called "No space left on device" ( ͡° ͜ʖ ͡°)
|
// A bit hacky but it works, unless someone has a file called "No space left on device" ( ͡° ͜ʖ ͡°)
|
||||||
export const isOutOfSpaceError = (err: InvariantExecaError) => (
|
export const isOutOfSpaceError = (err: InvariantExecaError) => (
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": ["@tsconfig/strictest", "@tsconfig/node18/tsconfig.json"],
|
"extends": ["@tsconfig/strictest", "@tsconfig/node20/tsconfig.json"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
},
|
},
|
||||||
|
|
156
yarn.lock
156
yarn.lock
|
@ -1804,6 +1804,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@sindresorhus/merge-streams@npm:^4.0.0":
|
||||||
|
version: 4.0.0
|
||||||
|
resolution: "@sindresorhus/merge-streams@npm:4.0.0"
|
||||||
|
checksum: 10/16551c787f5328c8ef05fd9831ade64369ccc992df78deb635ec6c44af217d2f1b43f8728c348cdc4e00585ff2fad6e00d8155199cbf6b154acc45fe65cbf0aa
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@szmarczak/http-timer@npm:^4.0.5":
|
"@szmarczak/http-timer@npm:^4.0.5":
|
||||||
version: 4.0.6
|
version: 4.0.6
|
||||||
resolution: "@szmarczak/http-timer@npm:4.0.6"
|
resolution: "@szmarczak/http-timer@npm:4.0.6"
|
||||||
|
@ -1843,6 +1850,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@tsconfig/node20@npm:^20.1.4":
|
||||||
|
version: 20.1.4
|
||||||
|
resolution: "@tsconfig/node20@npm:20.1.4"
|
||||||
|
checksum: 10/345dba8074647f6c11b8d78afa76d9c16e3436cb56a8e78fe2060014d33a09f3f4fd6ed81dc90e955d3509f926cd7fd61c6ddfd3d5a1d80758d7844f7cc3a99e
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@tsconfig/strictest@npm:^2.0.2":
|
"@tsconfig/strictest@npm:^2.0.2":
|
||||||
version: 2.0.2
|
version: 2.0.2
|
||||||
resolution: "@tsconfig/strictest@npm:2.0.2"
|
resolution: "@tsconfig/strictest@npm:2.0.2"
|
||||||
|
@ -2172,12 +2186,12 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/node@npm:18":
|
"@types/node@npm:20":
|
||||||
version: 18.19.21
|
version: 20.17.9
|
||||||
resolution: "@types/node@npm:18.19.21"
|
resolution: "@types/node@npm:20.17.9"
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: "npm:~5.26.4"
|
undici-types: "npm:~6.19.2"
|
||||||
checksum: 10/3a5c5841f294bc35b5b416a32764b5c0c2f22f4cef48cb7d2e3b4e068a52d5857e50da8e6e0685e743127c70344301c833849a3904ce3bd3f67448da5e85487a
|
checksum: 10/11604a47adf383892394a59a339136b2746a71addf4a3b13f661d23b6e81e8a4f3b35e69dbcffc94698368e5ab5ec056a43a86c87eff00b1b8ea8cfcbfe641df
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
@ -5430,6 +5444,26 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"execa@npm:^9.5.1":
|
||||||
|
version: 9.5.1
|
||||||
|
resolution: "execa@npm:9.5.1"
|
||||||
|
dependencies:
|
||||||
|
"@sindresorhus/merge-streams": "npm:^4.0.0"
|
||||||
|
cross-spawn: "npm:^7.0.3"
|
||||||
|
figures: "npm:^6.1.0"
|
||||||
|
get-stream: "npm:^9.0.0"
|
||||||
|
human-signals: "npm:^8.0.0"
|
||||||
|
is-plain-obj: "npm:^4.1.0"
|
||||||
|
is-stream: "npm:^4.0.1"
|
||||||
|
npm-run-path: "npm:^6.0.0"
|
||||||
|
pretty-ms: "npm:^9.0.0"
|
||||||
|
signal-exit: "npm:^4.1.0"
|
||||||
|
strip-final-newline: "npm:^4.0.0"
|
||||||
|
yoctocolors: "npm:^2.0.0"
|
||||||
|
checksum: 10/aa030cdd43ffbf6a8825c16eec1515729553ce3655a8fa5165f0ddab2320957a9783effbeff37662e238e6f5d979d9732e3baa4bcaaeba4360856e627a214177
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"expand-template@npm:^2.0.3":
|
"expand-template@npm:^2.0.3":
|
||||||
version: 2.0.3
|
version: 2.0.3
|
||||||
resolution: "expand-template@npm:2.0.3"
|
resolution: "expand-template@npm:2.0.3"
|
||||||
|
@ -5622,6 +5656,15 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"figures@npm:^6.1.0":
|
||||||
|
version: 6.1.0
|
||||||
|
resolution: "figures@npm:6.1.0"
|
||||||
|
dependencies:
|
||||||
|
is-unicode-supported: "npm:^2.0.0"
|
||||||
|
checksum: 10/9822d13630bee8e6a9f2da866713adf13854b07e0bfde042defa8bba32d47a1c0b2afa627ce73837c674cf9a5e3edce7e879ea72cb9ea7960b2390432d8e1167
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"file-entry-cache@npm:^6.0.1":
|
"file-entry-cache@npm:^6.0.1":
|
||||||
version: 6.0.1
|
version: 6.0.1
|
||||||
resolution: "file-entry-cache@npm:6.0.1"
|
resolution: "file-entry-cache@npm:6.0.1"
|
||||||
|
@ -6040,7 +6083,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"get-stream@npm:^9.0.1":
|
"get-stream@npm:^9.0.0, get-stream@npm:^9.0.1":
|
||||||
version: 9.0.1
|
version: 9.0.1
|
||||||
resolution: "get-stream@npm:9.0.1"
|
resolution: "get-stream@npm:9.0.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -6516,6 +6559,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"human-signals@npm:^8.0.0":
|
||||||
|
version: 8.0.0
|
||||||
|
resolution: "human-signals@npm:8.0.0"
|
||||||
|
checksum: 10/89acdc7081ac2a065e41cca7351c4b0fe2382e213b7372f90df6a554e340f31b49388a307adc1d6f4c60b2b4fe81eeff0bc1f44be6f5d844311cd92ccc7831c6
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"humanize-ms@npm:^1.2.1":
|
"humanize-ms@npm:^1.2.1":
|
||||||
version: 1.2.1
|
version: 1.2.1
|
||||||
resolution: "humanize-ms@npm:1.2.1"
|
resolution: "humanize-ms@npm:1.2.1"
|
||||||
|
@ -7016,7 +7066,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"is-plain-obj@npm:^4.0.0":
|
"is-plain-obj@npm:^4.0.0, is-plain-obj@npm:^4.1.0":
|
||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
resolution: "is-plain-obj@npm:4.1.0"
|
resolution: "is-plain-obj@npm:4.1.0"
|
||||||
checksum: 10/6dc45da70d04a81f35c9310971e78a6a3c7a63547ef782e3a07ee3674695081b6ca4e977fbb8efc48dae3375e0b34558d2bcd722aec9bddfa2d7db5b041be8ce
|
checksum: 10/6dc45da70d04a81f35c9310971e78a6a3c7a63547ef782e3a07ee3674695081b6ca4e977fbb8efc48dae3375e0b34558d2bcd722aec9bddfa2d7db5b041be8ce
|
||||||
|
@ -7111,6 +7161,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"is-unicode-supported@npm:^2.0.0":
|
||||||
|
version: 2.1.0
|
||||||
|
resolution: "is-unicode-supported@npm:2.1.0"
|
||||||
|
checksum: 10/f254e3da6b0ab1a57a94f7273a7798dd35d1d45b227759f600d0fa9d5649f9c07fa8d3c8a6360b0e376adf916d151ec24fc9a50c5295c58bae7ca54a76a063f9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"is-valid-glob@npm:^1.0.0":
|
"is-valid-glob@npm:^1.0.0":
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
resolution: "is-valid-glob@npm:1.0.0"
|
resolution: "is-valid-glob@npm:1.0.0"
|
||||||
|
@ -7605,6 +7662,7 @@ __metadata:
|
||||||
"@radix-ui/react-checkbox": "npm:^1.0.4"
|
"@radix-ui/react-checkbox": "npm:^1.0.4"
|
||||||
"@radix-ui/react-switch": "npm:^1.0.1"
|
"@radix-ui/react-switch": "npm:^1.0.1"
|
||||||
"@tsconfig/node18": "npm:^18.2.2"
|
"@tsconfig/node18": "npm:^18.2.2"
|
||||||
|
"@tsconfig/node20": "npm:^20.1.4"
|
||||||
"@tsconfig/strictest": "npm:^2.0.2"
|
"@tsconfig/strictest": "npm:^2.0.2"
|
||||||
"@tsconfig/vite-react": "npm:^3.0.0"
|
"@tsconfig/vite-react": "npm:^3.0.0"
|
||||||
"@types/color": "npm:^3.0.6"
|
"@types/color": "npm:^3.0.6"
|
||||||
|
@ -7618,7 +7676,7 @@ __metadata:
|
||||||
"@types/mime-types": "npm:^2.1.4"
|
"@types/mime-types": "npm:^2.1.4"
|
||||||
"@types/morgan": "npm:^1.9.9"
|
"@types/morgan": "npm:^1.9.9"
|
||||||
"@types/mousetrap": "npm:^1.6.15"
|
"@types/mousetrap": "npm:^1.6.15"
|
||||||
"@types/node": "npm:18"
|
"@types/node": "npm:20"
|
||||||
"@types/react": "npm:^18.2.66"
|
"@types/react": "npm:^18.2.66"
|
||||||
"@types/react-dom": "npm:^18.2.22"
|
"@types/react-dom": "npm:^18.2.22"
|
||||||
"@types/sortablejs": "npm:^1.15.0"
|
"@types/sortablejs": "npm:^1.15.0"
|
||||||
|
@ -7647,7 +7705,7 @@ __metadata:
|
||||||
eslint-plugin-react-hooks: "npm:^4.3.0"
|
eslint-plugin-react-hooks: "npm:^4.3.0"
|
||||||
eslint-plugin-unicorn: "npm:^51.0.1"
|
eslint-plugin-unicorn: "npm:^51.0.1"
|
||||||
evergreen-ui: "npm:^6.13.1"
|
evergreen-ui: "npm:^6.13.1"
|
||||||
execa: "npm:^8.0.1"
|
execa: "npm:^9.5.1"
|
||||||
express: "npm:^4.20.0"
|
express: "npm:^4.20.0"
|
||||||
express-async-handler: "npm:^1.2.0"
|
express-async-handler: "npm:^1.2.0"
|
||||||
fast-xml-parser: "npm:^4.4.1"
|
fast-xml-parser: "npm:^4.4.1"
|
||||||
|
@ -7698,7 +7756,7 @@ __metadata:
|
||||||
tiny-invariant: "npm:^1.3.3"
|
tiny-invariant: "npm:^1.3.3"
|
||||||
tsx: "npm:^4.7.1"
|
tsx: "npm:^4.7.1"
|
||||||
type-fest: "npm:^4.23.0"
|
type-fest: "npm:^4.23.0"
|
||||||
typescript: "npm:^5.5.4"
|
typescript: "npm:^5.7.2"
|
||||||
use-debounce: "npm:^5.1.0"
|
use-debounce: "npm:^5.1.0"
|
||||||
use-trace-update: "npm:^1.3.0"
|
use-trace-update: "npm:^1.3.0"
|
||||||
vite: "npm:^5.3.6"
|
vite: "npm:^5.3.6"
|
||||||
|
@ -8354,6 +8412,16 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"npm-run-path@npm:^6.0.0":
|
||||||
|
version: 6.0.0
|
||||||
|
resolution: "npm-run-path@npm:6.0.0"
|
||||||
|
dependencies:
|
||||||
|
path-key: "npm:^4.0.0"
|
||||||
|
unicorn-magic: "npm:^0.3.0"
|
||||||
|
checksum: 10/1a1b50aba6e6af7fd34a860ba2e252e245c4a59b316571a990356417c0cdf0414cabf735f7f52d9c330899cb56f0ab804a8e21fb12a66d53d7843e39ada4a3b6
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"npmlog@npm:^6.0.0":
|
"npmlog@npm:^6.0.0":
|
||||||
version: 6.0.2
|
version: 6.0.2
|
||||||
resolution: "npmlog@npm:6.0.2"
|
resolution: "npmlog@npm:6.0.2"
|
||||||
|
@ -8676,6 +8744,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"parse-ms@npm:^4.0.0":
|
||||||
|
version: 4.0.0
|
||||||
|
resolution: "parse-ms@npm:4.0.0"
|
||||||
|
checksum: 10/673c801d9f957ff79962d71ed5a24850163f4181a90dd30c4e3666b3a804f53b77f1f0556792e8b2adbb5d58757907d1aa51d7d7dc75997c2a56d72937cbc8b7
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"parse5-htmlparser2-tree-adapter@npm:^7.0.0":
|
"parse5-htmlparser2-tree-adapter@npm:^7.0.0":
|
||||||
version: 7.0.0
|
version: 7.0.0
|
||||||
resolution: "parse5-htmlparser2-tree-adapter@npm:7.0.0"
|
resolution: "parse5-htmlparser2-tree-adapter@npm:7.0.0"
|
||||||
|
@ -8912,6 +8987,15 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"pretty-ms@npm:^9.0.0":
|
||||||
|
version: 9.2.0
|
||||||
|
resolution: "pretty-ms@npm:9.2.0"
|
||||||
|
dependencies:
|
||||||
|
parse-ms: "npm:^4.0.0"
|
||||||
|
checksum: 10/a65a1d81560867f4f7128862fdbf0e1c2d3c5607bf75cae7758bf8111e2c4b744be46e084704125a38ba918bb43defa7a53aaff0f48c5c2d95367d3148c980d9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"prismjs@npm:^1.25.0":
|
"prismjs@npm:^1.25.0":
|
||||||
version: 1.26.0
|
version: 1.26.0
|
||||||
resolution: "prismjs@npm:1.26.0"
|
resolution: "prismjs@npm:1.26.0"
|
||||||
|
@ -10483,6 +10567,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"strip-final-newline@npm:^4.0.0":
|
||||||
|
version: 4.0.0
|
||||||
|
resolution: "strip-final-newline@npm:4.0.0"
|
||||||
|
checksum: 10/b5fe48f695d74863153a3b3155220e6e9bf51f4447832998c8edec38e6559b3af87a9fe5ac0df95570a78a26f5fa91701358842eab3c15480e27980b154a145f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"strip-indent@npm:^3.0.0":
|
"strip-indent@npm:^3.0.0":
|
||||||
version: 3.0.0
|
version: 3.0.0
|
||||||
resolution: "strip-indent@npm:3.0.0"
|
resolution: "strip-indent@npm:3.0.0"
|
||||||
|
@ -11068,7 +11159,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"typescript@npm:^5.0.4, typescript@npm:^5.5.4":
|
"typescript@npm:^5.0.4":
|
||||||
version: 5.5.4
|
version: 5.5.4
|
||||||
resolution: "typescript@npm:5.5.4"
|
resolution: "typescript@npm:5.5.4"
|
||||||
bin:
|
bin:
|
||||||
|
@ -11078,6 +11169,16 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"typescript@npm:^5.7.2":
|
||||||
|
version: 5.7.2
|
||||||
|
resolution: "typescript@npm:5.7.2"
|
||||||
|
bin:
|
||||||
|
tsc: bin/tsc
|
||||||
|
tsserver: bin/tsserver
|
||||||
|
checksum: 10/4caa3904df69db9d4a8bedc31bafc1e19ffb7b24fbde2997a1633ae1398d0de5bdbf8daf602ccf3b23faddf1aeeb9b795223a2ed9c9a4fdcaf07bfde114a401a
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"typescript@patch:typescript@npm%3A^4.0.2#optional!builtin<compat/typescript>":
|
"typescript@patch:typescript@npm%3A^4.0.2#optional!builtin<compat/typescript>":
|
||||||
version: 4.9.5
|
version: 4.9.5
|
||||||
resolution: "typescript@patch:typescript@npm%3A4.9.5#optional!builtin<compat/typescript>::version=4.9.5&hash=289587"
|
resolution: "typescript@patch:typescript@npm%3A4.9.5#optional!builtin<compat/typescript>::version=4.9.5&hash=289587"
|
||||||
|
@ -11088,7 +11189,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"typescript@patch:typescript@npm%3A^5.0.4#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.5.4#optional!builtin<compat/typescript>":
|
"typescript@patch:typescript@npm%3A^5.0.4#optional!builtin<compat/typescript>":
|
||||||
version: 5.5.4
|
version: 5.5.4
|
||||||
resolution: "typescript@patch:typescript@npm%3A5.5.4#optional!builtin<compat/typescript>::version=5.5.4&hash=379a07"
|
resolution: "typescript@patch:typescript@npm%3A5.5.4#optional!builtin<compat/typescript>::version=5.5.4&hash=379a07"
|
||||||
bin:
|
bin:
|
||||||
|
@ -11098,6 +11199,16 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"typescript@patch:typescript@npm%3A^5.7.2#optional!builtin<compat/typescript>":
|
||||||
|
version: 5.7.2
|
||||||
|
resolution: "typescript@patch:typescript@npm%3A5.7.2#optional!builtin<compat/typescript>::version=5.7.2&hash=74658d"
|
||||||
|
bin:
|
||||||
|
tsc: bin/tsc
|
||||||
|
tsserver: bin/tsserver
|
||||||
|
checksum: 10/ff27fc124bceb8969be722baa38af945b2505767cf794de3e2715e58f61b43780284060287d651fcbbdfb6f917f4653b20f4751991f17e0706db389b9bb3f75d
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"ua-parser-js@npm:^0.7.30":
|
"ua-parser-js@npm:^0.7.30":
|
||||||
version: 0.7.33
|
version: 0.7.33
|
||||||
resolution: "ua-parser-js@npm:0.7.33"
|
resolution: "ua-parser-js@npm:0.7.33"
|
||||||
|
@ -11154,6 +11265,20 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"undici-types@npm:~6.19.2":
|
||||||
|
version: 6.19.8
|
||||||
|
resolution: "undici-types@npm:6.19.8"
|
||||||
|
checksum: 10/cf0b48ed4fc99baf56584afa91aaffa5010c268b8842f62e02f752df209e3dea138b372a60a963b3b2576ed932f32329ce7ddb9cb5f27a6c83040d8cd74b7a70
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"unicorn-magic@npm:^0.3.0":
|
||||||
|
version: 0.3.0
|
||||||
|
resolution: "unicorn-magic@npm:0.3.0"
|
||||||
|
checksum: 10/bdd7d7c522f9456f32a0b77af23f8854f9a7db846088c3868ec213f9550683ab6a2bdf3803577eacbafddb4e06900974385841ccb75338d17346ccef45f9cb01
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"unique-filename@npm:^2.0.0":
|
"unique-filename@npm:^2.0.0":
|
||||||
version: 2.0.1
|
version: 2.0.1
|
||||||
resolution: "unique-filename@npm:2.0.1"
|
resolution: "unique-filename@npm:2.0.1"
|
||||||
|
@ -11761,6 +11886,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"yoctocolors@npm:^2.0.0":
|
||||||
|
version: 2.1.1
|
||||||
|
resolution: "yoctocolors@npm:2.1.1"
|
||||||
|
checksum: 10/563fbec88bce9716d1044bc98c96c329e1d7a7c503e6f1af68f1ff914adc3ba55ce953c871395e2efecad329f85f1632f51a99c362032940321ff80c42a6f74d
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"zod@npm:^3.22.5":
|
"zod@npm:^3.22.5":
|
||||||
version: 3.22.5
|
version: 3.22.5
|
||||||
resolution: "zod@npm:3.22.5"
|
resolution: "zod@npm:3.22.5"
|
||||||
|
|
Ładowanie…
Reference in New Issue