import React, { memo, Fragment } from 'react'; import { FaVideo, FaVideoSlash, FaFileExport, FaFileImport, FaVolumeUp, FaVolumeMute, FaBan, FaTrashAlt, FaInfoCircle } from 'react-icons/fa'; import { GoFileBinary } from 'react-icons/go'; import { MdSubtitles } from 'react-icons/md'; import Swal from 'sweetalert2'; import { SegmentedControl } from 'evergreen-ui'; import withReactContent from 'sweetalert2-react-content'; const ReactSwal = withReactContent(Swal); const { formatDuration } = require('./util'); const { getStreamFps } = require('./ffmpeg'); function onInfoClick(s, title) { ReactSwal.fire({ showCloseButton: true, title, html:
{JSON.stringify(s, null, 2)}
, }); } const Stream = memo(({ stream, onToggle, copyStream, fileDuration }) => { const bitrate = parseInt(stream.bit_rate, 10); const streamDuration = parseInt(stream.duration, 10); const duration = !Number.isNaN(streamDuration) ? streamDuration : fileDuration; let Icon; if (stream.codec_type === 'audio') { Icon = copyStream ? FaVolumeUp : FaVolumeMute; } else if (stream.codec_type === 'video') { Icon = copyStream ? FaVideo : FaVideoSlash; } else if (stream.codec_type === 'subtitle') { Icon = copyStream ? MdSubtitles : FaBan; } else { Icon = copyStream ? GoFileBinary : FaBan; } const streamFps = getStreamFps(stream); const onClick = () => onToggle && onToggle(stream.index); return ( {stream.index} {stream.codec_type} {stream.codec_tag !== '0x0000' && stream.codec_tag_string} {stream.codec_name} {!Number.isNaN(duration) && `${formatDuration({ seconds: duration })}`} {stream.nb_frames} {!Number.isNaN(bitrate) && `${(bitrate / 1e6).toFixed(1)}MBit/s`} {stream.width && stream.height && `${stream.width}x${stream.height}`} {stream.channels && `${stream.channels}c`} {stream.channel_layout} {streamFps && `${streamFps.toFixed(2)}fps`} onInfoClick(stream, 'Stream info')} size={26} /> ); }); function renderFileRow(path, formatData, onTrashClick) { return ( {onTrashClick && } {path.replace(/.*\/([^/]+)$/, '$1')} onInfoClick(formatData, 'File info')} size={26} /> ); } const StreamsSelector = memo(({ mainFilePath, mainFileFormatData, streams: existingStreams, isCopyingStreamId, toggleCopyStreamId, setCopyStreamIdsForPath, onExtractAllStreamsPress, externalFiles, setExternalFiles, showAddStreamSourceDialog, shortestFlag, setShortestFlag, nonCopiedExtraStreams, areWeCutting, AutoExportToggler, }) => { if (!existingStreams) return null; function getFormatDuration(formatData) { if (!formatData || !formatData.duration) return undefined; const parsed = parseFloat(formatData.duration, 10); if (Number.isNaN(parsed)) return undefined; return parsed; } async function removeFile(path) { setCopyStreamIdsForPath(path, () => ({})); setExternalFiles((old) => { const { [path]: val, ...rest } = old; return rest; }); } const externalFilesEntries = Object.entries(externalFiles); return (

Click to select which tracks to keep when exporting:

{renderFileRow(mainFilePath, mainFileFormatData)} {existingStreams.map((stream) => ( toggleCopyStreamId(mainFilePath, streamId)} fileDuration={getFormatDuration(mainFileFormatData)} /> ))} {externalFilesEntries.map(([path, { streams, formatData }]) => ( {renderFileRow(path, formatData, () => removeFile(path))} {streams.map((stream) => ( toggleCopyStreamId(path, streamId)} fileDuration={getFormatDuration(formatData)} /> ))} ))}
Keep? Type Tag Codec Duration Frames Bitrate Data
{externalFilesEntries.length > 0 && !areWeCutting && (
If the streams have different length, do you want to make the combined output file as long as the longest stream or the shortest stream?
setShortestFlag(value === 'shortest')} />
)} {nonCopiedExtraStreams.length > 0 && (
Discard or extract unprocessable tracks to separate files?
)}
Include more tracks from other file
{externalFilesEntries.length === 0 && (
Export each track as individual files
)}
); }); export default StreamsSelector;