mirror of
https://github.com/mifi/lossless-cut.git
synced 2024-11-21 18:02:35 +01:00
parent
08e29b2135
commit
cfd51d6ce5
@ -39,6 +39,7 @@
|
||||
},
|
||||
"license": "GPL-2.0-only",
|
||||
"devDependencies": {
|
||||
"@adamscybot/react-leaflet-component-marker": "^2.0.0",
|
||||
"@fontsource/open-sans": "^4.5.14",
|
||||
"@radix-ui/colors": "^0.1.8",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
@ -50,6 +51,7 @@
|
||||
"@types/css-modules": "^1.0.5",
|
||||
"@types/eslint": "^8",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/leaflet": "^1",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@types/morgan": "^1.9.9",
|
||||
@ -85,6 +87,7 @@
|
||||
"icon-gen": "^4.0.0",
|
||||
"immer": "^10.0.2",
|
||||
"ky": "^0.33.1",
|
||||
"leaflet": "^1.9.4",
|
||||
"luxon": "^3.3.0",
|
||||
"mkdirp": "^1.0.3",
|
||||
"mousetrap": "^1.6.5",
|
||||
@ -97,6 +100,7 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-i18next": "^12.1.5",
|
||||
"react-icons": "^4.1.0",
|
||||
"react-leaflet": "^4.2.1",
|
||||
"react-lottie-player": "^1.5.0",
|
||||
"react-sortablejs": "^6.1.4",
|
||||
"react-syntax-highlighter": "^15.4.3",
|
||||
|
@ -12,11 +12,12 @@ import Select from './components/Select';
|
||||
import { showJson5Dialog } from './dialogs';
|
||||
import { getStreamFps } from './ffmpeg';
|
||||
import { deleteDispositionValue } from './util';
|
||||
import { getActiveDisposition, attachedPicDisposition } from './util/streams';
|
||||
import { getActiveDisposition, attachedPicDisposition, isGpsStream } from './util/streams';
|
||||
import TagEditor from './components/TagEditor';
|
||||
import { FFprobeChapter, FFprobeFormat, FFprobeStream } from '../../../ffprobe';
|
||||
import { CustomTagsByFile, FilesMeta, FormatTimecode, ParamsByStreamId, StreamParams } from './types';
|
||||
import useUserSettings from './hooks/useUserSettings';
|
||||
import tryShowGpsMap from './gps';
|
||||
|
||||
|
||||
const dispositionOptions = ['default', 'dub', 'original', 'comment', 'lyrics', 'karaoke', 'forced', 'hearing_impaired', 'visual_impaired', 'clean_effects', 'attached_pic', 'captions', 'descriptions', 'dependent', 'metadata'];
|
||||
@ -209,6 +210,10 @@ const Stream = memo(({ filePath, stream, onToggle, batchSetCopyStreamIds, copySt
|
||||
loadSubtitleTrackToSegments?.(stream.index);
|
||||
}, [loadSubtitleTrackToSegments, stream.index]);
|
||||
|
||||
const onLoadGpsTrackClick = useCallback(async () => {
|
||||
await tryShowGpsMap(filePath, stream.index);
|
||||
}, [filePath, stream.index]);
|
||||
|
||||
const codecTag = stream.codec_tag !== '0x0000' && stream.codec_tag_string;
|
||||
|
||||
return (
|
||||
@ -260,6 +265,11 @@ const Stream = memo(({ filePath, stream, onToggle, batchSetCopyStreamIds, copySt
|
||||
{t('Create segments from subtitles')}
|
||||
</Menu.Item>
|
||||
)}
|
||||
{isGpsStream(stream) && (
|
||||
<Menu.Item icon={<MdSubtitles color="black" />} onClick={onLoadGpsTrackClick}>
|
||||
{t('Show GPS map')}
|
||||
</Menu.Item>
|
||||
)}
|
||||
</Menu.Group>
|
||||
<Menu.Divider />
|
||||
<Menu.Group>
|
||||
|
@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url';
|
||||
import { it, describe, expect } from 'vitest';
|
||||
|
||||
|
||||
import { parseSrt, formatSrt, parseYouTube, formatYouTube, parseMplayerEdl, parseXmeml, parseFcpXml, parseCsv, parseCsvTime, getFrameValParser, formatCsvFrames, getFrameCountRaw, parsePbf, parseDvAnalyzerSummaryTxt, parseCutlist } from './edlFormats';
|
||||
import { parseSrtToSegments, formatSrt, parseYouTube, formatYouTube, parseMplayerEdl, parseXmeml, parseFcpXml, parseCsv, parseCsvTime, getFrameValParser, formatCsvFrames, getFrameCountRaw, parsePbf, parseDvAnalyzerSummaryTxt, parseCutlist } from './edlFormats';
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
@ -306,11 +306,11 @@ it('parses pbf', async () => {
|
||||
});
|
||||
|
||||
it('parses srt', async () => {
|
||||
expect(parseSrt(await readFixture('sample.srt'))).toMatchSnapshot();
|
||||
expect(parseSrtToSegments(await readFixture('sample.srt'))).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('format srt', async () => {
|
||||
expect(formatSrt(parseSrt(await readFixture('sample.srt')))).toMatchSnapshot();
|
||||
expect(formatSrt(parseSrtToSegments(await readFixture('sample.srt')))).toMatchSnapshot();
|
||||
});
|
||||
|
||||
// https://github.com/mifi/lossless-cut/issues/1664
|
||||
|
@ -370,7 +370,7 @@ export function parseDvAnalyzerSummaryTxt(txt: string) {
|
||||
|
||||
// http://www.textfiles.com/uploads/kds-srt.txt
|
||||
export function parseSrt(text: string) {
|
||||
const ret: { start?: number, end?: number, name: string, tags: Record<string, string | undefined> }[] = [];
|
||||
const ret: { start: number, end: number, lines: string[], index: number | undefined }[] = [];
|
||||
|
||||
// working state
|
||||
let subtitleIndexAt: number | undefined;
|
||||
@ -380,7 +380,7 @@ export function parseSrt(text: string) {
|
||||
|
||||
const flush = () => {
|
||||
if (start != null && end != null && lines.length > 0) {
|
||||
ret.push({ start, end, name: lines.join('\r\n'), tags: { index: subtitleIndexAt != null ? String(subtitleIndexAt) : undefined } });
|
||||
ret.push({ start, end, lines, index: subtitleIndexAt });
|
||||
}
|
||||
start = undefined;
|
||||
end = undefined;
|
||||
@ -396,7 +396,7 @@ export function parseSrt(text: string) {
|
||||
} else if (subtitleIndexAt != null && subtitleIndexAt > 0) {
|
||||
const match = line.match(/^(\d+:\d+:\d+[,.]\d+\s+)-->(\s+\d+:\d+:\d+[,.]\d+)$/);
|
||||
if (match) {
|
||||
const fixComma = (v) => v.replaceAll(',', '.');
|
||||
const fixComma = (v: string | undefined) => v!.replaceAll(',', '.');
|
||||
start = parseTime(fixComma(match[1]))?.time;
|
||||
end = parseTime(fixComma(match[2]))?.time;
|
||||
} else if (start != null && end != null) {
|
||||
@ -415,6 +415,15 @@ export function parseSrt(text: string) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function parseSrtToSegments(text: string) {
|
||||
return parseSrt(text).map(({ start, end, lines, index }) => ({
|
||||
start,
|
||||
end,
|
||||
name: lines.join('\r\n'),
|
||||
tags: { index: index != null ? String(index) : undefined },
|
||||
}));
|
||||
}
|
||||
|
||||
export function formatSrt(segments) {
|
||||
return segments.reduce((acc, segment, index) => `${acc}${index > 0 ? '\r\n' : ''}${index + 1}\r\n${formatDuration({ seconds: segment.start }).replaceAll('.', ',')} --> ${formatDuration({ seconds: segment.end }).replaceAll('.', ',')}\r\n${segment.name || '-'}\r\n`, '');
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import i18n from 'i18next';
|
||||
import type { parse as CueParse } from 'cue-parser';
|
||||
import invariant from 'tiny-invariant';
|
||||
|
||||
import { parseSrt, formatSrt, parseCuesheet, parseXmeml, parseFcpXml, parseCsv, parseCutlist, parsePbf, parseMplayerEdl, formatCsvHuman, formatTsv, formatCsvFrames, formatCsvSeconds, parseCsvTime, getFrameValParser, parseDvAnalyzerSummaryTxt } from './edlFormats';
|
||||
import { parseSrtToSegments, formatSrt, parseCuesheet, parseXmeml, parseFcpXml, parseCsv, parseCutlist, parsePbf, parseMplayerEdl, formatCsvHuman, formatTsv, formatCsvFrames, formatCsvSeconds, parseCsvTime, getFrameValParser, parseDvAnalyzerSummaryTxt } from './edlFormats';
|
||||
import { askForYouTubeInput, showOpenDialog } from './dialogs';
|
||||
import { getOutPath } from './util';
|
||||
import { EdlExportType, EdlFileType, EdlImportType, Segment, StateSegment } from './types';
|
||||
@ -52,7 +52,7 @@ export async function loadCue(path: string) {
|
||||
}
|
||||
|
||||
export async function loadSrt(path: string) {
|
||||
return parseSrt(await readFile(path, 'utf8'));
|
||||
return parseSrtToSegments(await readFile(path, 'utf8'));
|
||||
}
|
||||
|
||||
export async function saveCsv(path: string, cutSegments) {
|
||||
|
@ -9,7 +9,7 @@ import { pcmAudioCodecs, getMapStreamsArgs, isMov, LiteFFprobeStream } from './u
|
||||
import { getSuffixedOutPath, isExecaError } from './util';
|
||||
import { isDurationValid } from './segments';
|
||||
import { FFprobeChapter, FFprobeFormat, FFprobeProbeResult, FFprobeStream } from '../../../ffprobe';
|
||||
import { parseSrt } from './edlFormats';
|
||||
import { parseSrt, parseSrtToSegments } from './edlFormats';
|
||||
|
||||
const FileType = window.require('file-type');
|
||||
const { pathExists } = window.require('fs-extra');
|
||||
@ -505,7 +505,7 @@ async function renderThumbnail(filePath: string, timestamp: number) {
|
||||
return URL.createObjectURL(blob);
|
||||
}
|
||||
|
||||
export async function extractSubtitleTrackToSegments(filePath: string, streamId: number) {
|
||||
export async function extractSubtitleTrack(filePath: string, streamId: number) {
|
||||
const args = [
|
||||
'-hide_banner',
|
||||
'-i', filePath,
|
||||
@ -515,7 +515,17 @@ export async function extractSubtitleTrackToSegments(filePath: string, streamId:
|
||||
];
|
||||
|
||||
const { stdout } = await runFfmpeg(args);
|
||||
return parseSrt(stdout.toString('utf8'));
|
||||
return stdout.toString('utf8');
|
||||
}
|
||||
|
||||
export async function extractSubtitleTrackToSegments(filePath: string, streamId: number) {
|
||||
const srt = await extractSubtitleTrack(filePath, streamId);
|
||||
return parseSrtToSegments(srt);
|
||||
}
|
||||
|
||||
export async function extractSrtGpsTrack(filePath: string, streamId: number) {
|
||||
const srt = await extractSubtitleTrack(filePath, streamId);
|
||||
return parseSrt(srt);
|
||||
}
|
||||
|
||||
export async function extractSubtitleTrackVtt(filePath: string, streamId: number) {
|
||||
@ -641,7 +651,7 @@ function parseTimecode(str: string, frameRate?: number | undefined) {
|
||||
|
||||
export function getTimecodeFromStreams(streams: FFprobeStream[]) {
|
||||
console.log('Trying to load timecode');
|
||||
let foundTimecode;
|
||||
let foundTimecode: number | undefined;
|
||||
streams.find((stream) => {
|
||||
try {
|
||||
if (stream.tags && stream.tags['timecode']) {
|
||||
|
75
src/renderer/src/gps.tsx
Normal file
75
src/renderer/src/gps.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import { MapContainer, Popup, TileLayer } from 'react-leaflet';
|
||||
import { Marker } from '@adamscybot/react-leaflet-component-marker';
|
||||
import { Trans } from 'react-i18next';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import { FaMapMarkerAlt } from 'react-icons/fa';
|
||||
|
||||
import { extractSrtGpsTrack } from './ffmpeg';
|
||||
import { ReactSwal } from './swal';
|
||||
import { handleError } from './util';
|
||||
|
||||
|
||||
export default async function tryShowGpsMap(filePath: string, streamIndex: number) {
|
||||
try {
|
||||
const subtitles = await extractSrtGpsTrack(filePath, streamIndex);
|
||||
const gpsPoints = subtitles.flatMap((subtitle) => {
|
||||
const firstLine = subtitle.lines[0];
|
||||
// example:
|
||||
// "F/2.8, SS 776.89, ISO 100, EV -1.0, GPS (15.0732, 67.9771, 19), D 67.78m, H 20.30m, H.S 1.03m/s, V.S 0.00m/s"
|
||||
const gpsMatch = firstLine?.match(/^\s*([^,]+),\s*SS\s+([^,]+),\s*ISO\s+([^,]+),\s*EV\s+([^,]+),\s*GPS\s+\(([^,]+),\s*([^,]+),\s*([^,]+)\),\s*D\s+([^m]+)m,\s*H\s+([^m]+)m,\s*H\.S\s+([^m]+)m\/s,\s*V\.S\s+([^m]+)m\/s\s*$/);
|
||||
if (!gpsMatch || firstLine == null) return [];
|
||||
return [{
|
||||
index: subtitle.index,
|
||||
raw: firstLine,
|
||||
f: gpsMatch[1]!,
|
||||
ss: parseFloat(gpsMatch![2]!),
|
||||
iso: parseInt(gpsMatch![3]!, 10),
|
||||
ev: parseFloat(gpsMatch![4]!),
|
||||
lat: parseFloat(gpsMatch![5]!),
|
||||
lng: parseFloat(gpsMatch![6]!),
|
||||
alt: parseFloat(gpsMatch![7]!),
|
||||
distance: parseFloat(gpsMatch![8]!),
|
||||
height: parseFloat(gpsMatch![9]!),
|
||||
horizontalSpeed: parseFloat(gpsMatch![10]!),
|
||||
verticalSpeed: parseFloat(gpsMatch![11]!),
|
||||
}];
|
||||
});
|
||||
// console.log(gpsPoints)
|
||||
|
||||
const firstPoint = gpsPoints[0];
|
||||
|
||||
if (firstPoint == null) {
|
||||
throw new Error('No GPS points found');
|
||||
}
|
||||
|
||||
// https://www.openstreetmap.org/copyright
|
||||
ReactSwal.fire({
|
||||
width: '100%',
|
||||
html: (
|
||||
<>
|
||||
<div style={{ marginBottom: '1em' }}><Trans>GPS track</Trans></div>
|
||||
|
||||
<MapContainer center={[firstPoint.lng, firstPoint.lat]} zoom={16} style={{ height: 500 }}>
|
||||
<TileLayer
|
||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
/>
|
||||
|
||||
{gpsPoints.map((point, i) => (
|
||||
<Marker key={point.index} position={[point.lng, point.lat]} icon={<FaMapMarkerAlt color="#af0e0e" size={20} />}>
|
||||
<Popup>
|
||||
<div>Point {i + 1} / {gpsPoints.length}</div>
|
||||
{point.raw}
|
||||
</Popup>
|
||||
</Marker>
|
||||
))}
|
||||
</MapContainer>
|
||||
</>
|
||||
),
|
||||
showCloseButton: true,
|
||||
showConfirmButton: false,
|
||||
});
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
}
|
||||
}
|
@ -232,7 +232,7 @@ export function shouldCopyStreamByDefault(stream: FFprobeStream) {
|
||||
|
||||
export const attachedPicDisposition = 'attached_pic';
|
||||
|
||||
export type LiteFFprobeStream = Pick<FFprobeStream, 'index' | 'codec_type' | 'codec_tag' | 'codec_name' | 'disposition'>;
|
||||
export type LiteFFprobeStream = Pick<FFprobeStream, 'index' | 'codec_type' | 'codec_tag' | 'codec_name' | 'disposition' | 'tags'>;
|
||||
|
||||
export function isStreamThumbnail(stream: LiteFFprobeStream) {
|
||||
return stream && stream.codec_type === 'video' && stream.disposition?.[attachedPicDisposition] === 1;
|
||||
@ -241,6 +241,7 @@ export function isStreamThumbnail(stream: LiteFFprobeStream) {
|
||||
export const getAudioStreams = <T extends LiteFFprobeStream>(streams: T[]) => streams.filter((stream) => stream.codec_type === 'audio');
|
||||
export const getRealVideoStreams = <T extends LiteFFprobeStream>(streams: T[]) => streams.filter((stream) => stream.codec_type === 'video' && !isStreamThumbnail(stream));
|
||||
export const getSubtitleStreams = <T extends LiteFFprobeStream>(streams: T[]) => streams.filter((stream) => stream.codec_type === 'subtitle');
|
||||
export const isGpsStream = <T extends LiteFFprobeStream>(stream: T) => stream.codec_type === 'subtitle' && stream.tags?.['handler_name'] === '\u0010DJI.Subtitle';
|
||||
|
||||
// videoTracks/audioTracks seems to be 1-indexed, while ffmpeg is 0-indexes
|
||||
const getHtml5TrackId = (ffmpegTrackIndex: number) => String(ffmpegTrackIndex + 1);
|
||||
|
86
yarn.lock
86
yarn.lock
@ -19,6 +19,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@adamscybot/react-leaflet-component-marker@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "@adamscybot/react-leaflet-component-marker@npm:2.0.0"
|
||||
dependencies:
|
||||
react-is: "npm:^18.0.0"
|
||||
react-reverse-portal: "npm:^2.1.2"
|
||||
type-fest: "npm:^4.10.3"
|
||||
peerDependencies:
|
||||
leaflet: ^1.9.0
|
||||
react: ^18.0.0
|
||||
react-dom: ^18.0.0
|
||||
react-leaflet: ^4.0.0
|
||||
checksum: a5f62246afa519ded23b7f273a3fee36cf6c13782db97add07681234acc01e1e943c33b23ba8aacb73e2481891b14640645d901d55cf6d5322e75dd3dd10c2c8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ampproject/remapping@npm:^2.2.0, @ampproject/remapping@npm:^2.3.0":
|
||||
version: 2.3.0
|
||||
resolution: "@ampproject/remapping@npm:2.3.0"
|
||||
@ -1617,6 +1633,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@react-leaflet/core@npm:^2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "@react-leaflet/core@npm:2.1.0"
|
||||
peerDependencies:
|
||||
leaflet: ^1.9.0
|
||||
react: ^18.0.0
|
||||
react-dom: ^18.0.0
|
||||
checksum: 12ce28b85cf6712a1a7b7c49466b941fc619bc7b1535308bc5711a35f7e89eb16298babfd62f6b3a92e64abf94dcf517b2bc460f59fcf20599821bc6ab3b3048
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rollup/rollup-android-arm-eabi@npm:4.19.0":
|
||||
version: 4.19.0
|
||||
resolution: "@rollup/rollup-android-arm-eabi@npm:4.19.0"
|
||||
@ -1954,6 +1981,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/geojson@npm:*":
|
||||
version: 7946.0.14
|
||||
resolution: "@types/geojson@npm:7946.0.14"
|
||||
checksum: ae511bee6488ae3bd5a3a3347aedb0371e997b14225b8983679284e22fa4ebd88627c6e3ff8b08bf4cc35068cb29310c89427311ffc9322c255615821a922e71
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/hast@npm:^2.0.0":
|
||||
version: 2.3.4
|
||||
resolution: "@types/hast@npm:2.3.4"
|
||||
@ -2007,6 +2041,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/leaflet@npm:^1":
|
||||
version: 1.9.12
|
||||
resolution: "@types/leaflet@npm:1.9.12"
|
||||
dependencies:
|
||||
"@types/geojson": "npm:*"
|
||||
checksum: ff6dce2f613b97bdc3ceb929e6eeaaa8bef8bbafdf9758935b1d679cbaf76360e366080d77e42da58e41aac146434c5d18c70ec919d37e01e0592f0a4f2e967e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/lodash@npm:^4.14.202":
|
||||
version: 4.14.202
|
||||
resolution: "@types/lodash@npm:4.14.202"
|
||||
@ -7414,6 +7457,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"leaflet@npm:^1.9.4":
|
||||
version: 1.9.4
|
||||
resolution: "leaflet@npm:1.9.4"
|
||||
checksum: 7b6a74d503980961a85bdabebf9d1162c26db0e88195800ceea311682b653d621718f2ada3c9aab903a735af9862c9ae278ba550d4429acbd954d43449cd0d77
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"levn@npm:^0.4.1":
|
||||
version: 0.4.1
|
||||
resolution: "levn@npm:0.4.1"
|
||||
@ -7552,6 +7602,7 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "lossless-cut@workspace:."
|
||||
dependencies:
|
||||
"@adamscybot/react-leaflet-component-marker": "npm:^2.0.0"
|
||||
"@electron/remote": "npm:^2.0.10"
|
||||
"@fontsource/open-sans": "npm:^4.5.14"
|
||||
"@octokit/core": "npm:5"
|
||||
@ -7565,6 +7616,7 @@ __metadata:
|
||||
"@types/css-modules": "npm:^1.0.5"
|
||||
"@types/eslint": "npm:^8"
|
||||
"@types/express": "npm:^4.17.21"
|
||||
"@types/leaflet": "npm:^1"
|
||||
"@types/lodash": "npm:^4.14.202"
|
||||
"@types/luxon": "npm:^3.4.2"
|
||||
"@types/morgan": "npm:^1.9.9"
|
||||
@ -7611,6 +7663,7 @@ __metadata:
|
||||
immer: "npm:^10.0.2"
|
||||
json5: "npm:^2.2.2"
|
||||
ky: "npm:^0.33.1"
|
||||
leaflet: "npm:^1.9.4"
|
||||
lodash: "npm:^4.17.19"
|
||||
luxon: "npm:^3.3.0"
|
||||
mime-types: "npm:^2.1.14"
|
||||
@ -7626,6 +7679,7 @@ __metadata:
|
||||
react-dom: "npm:^18.2.0"
|
||||
react-i18next: "npm:^12.1.5"
|
||||
react-icons: "npm:^4.1.0"
|
||||
react-leaflet: "npm:^4.2.1"
|
||||
react-lottie-player: "npm:^1.5.0"
|
||||
react-sortablejs: "npm:^6.1.4"
|
||||
react-syntax-highlighter: "npm:^15.4.3"
|
||||
@ -9111,6 +9165,26 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-is@npm:^18.0.0":
|
||||
version: 18.3.1
|
||||
resolution: "react-is@npm:18.3.1"
|
||||
checksum: d5f60c87d285af24b1e1e7eaeb123ec256c3c8bdea7061ab3932e3e14685708221bf234ec50b21e10dd07f008f1b966a2730a0ce4ff67905b3872ff2042aec22
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-leaflet@npm:^4.2.1":
|
||||
version: 4.2.1
|
||||
resolution: "react-leaflet@npm:4.2.1"
|
||||
dependencies:
|
||||
"@react-leaflet/core": "npm:^2.1.0"
|
||||
peerDependencies:
|
||||
leaflet: ^1.9.0
|
||||
react: ^18.0.0
|
||||
react-dom: ^18.0.0
|
||||
checksum: 01cee12dc32e86d0153c989894fdba1c5c50fa41ad8d712352fa616f7b0dd32844aa17bb06c66f7569133675e2946c669090b4c496610cce3fa37c22254ca89f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-lottie-player@npm:^1.5.0":
|
||||
version: 1.5.0
|
||||
resolution: "react-lottie-player@npm:1.5.0"
|
||||
@ -9131,6 +9205,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-reverse-portal@npm:^2.1.2":
|
||||
version: 2.1.2
|
||||
resolution: "react-reverse-portal@npm:2.1.2"
|
||||
peerDependencies:
|
||||
react: ^16.0.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0
|
||||
checksum: 4b24fdb1a6727b6585aa63a03ef0e366064f062418555d847ef53645ee3ea91346481897eb90ab5f6b7da2898096da54d6fbde949bc159a8f4296e3b4ba5d904
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-sortablejs@npm:^6.1.4":
|
||||
version: 6.1.4
|
||||
resolution: "react-sortablejs@npm:6.1.4"
|
||||
@ -10951,7 +11035,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"type-fest@npm:^4.23.0":
|
||||
"type-fest@npm:^4.10.3, type-fest@npm:^4.23.0":
|
||||
version: 4.23.0
|
||||
resolution: "type-fest@npm:4.23.0"
|
||||
checksum: c411dea83262f9a4453e09ff82e3ac729dd26afc2e68b7a9fe93dd633b1a2bf7bf2bf3e041676497ae8b8411266019b3add91d4fe34b926a82ba09eb41e9e52b
|
||||
|
Loading…
Reference in New Issue
Block a user