Merge branch 'master1'

3.68.0.1
nieve 4 months ago
commit 31bb9357fe

5
.github/pull.yml vendored

@ -0,0 +1,5 @@
version: "1"
rules:
- base: master
upstream: mifi:master # change `wei` to the owner of upstream repo
mergeMethod: merge

@ -4,8 +4,8 @@ on:
push: push:
tags: tags:
- 'v*' - 'v*'
schedule: # schedule:
- cron: '0 10 * * *' # - cron: '0 10 * * *'
workflow_dispatch: workflow_dispatch:
inputs: inputs:
tmate_enabled: tmate_enabled:
@ -25,8 +25,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [macos-latest, ubuntu-latest, windows-latest, windows-11-arm] os: [ ubuntu-latest, windows-latest]
# os: [windows-latest]
steps: steps:
- name: Setup tmate session - name: Setup tmate session

@ -1,3 +1,7 @@
Releases: https://github.com/ac615223s5/lossless-cut/releases
Forked from https://github.com/mifi/lossless-cut/
<div align="center"> <div align="center">
<br> <br>
<p><a href="https://mifi.no/losslesscut/"><img src="src/renderer/src/icon.svg" width="120" alt="LosslessCut" /></a></p> <p><a href="https://mifi.no/losslesscut/"><img src="src/renderer/src/icon.svg" width="120" alt="LosslessCut" /></a></p>

@ -334,16 +334,16 @@ export async function askForAlignSegments() {
const startOrEnd = await askForSegmentsStartOrEnd(i18n.t('Do you want to align the segment start or end timestamps to keyframes?')); const startOrEnd = await askForSegmentsStartOrEnd(i18n.t('Do you want to align the segment start or end timestamps to keyframes?'));
if (startOrEnd == null) return undefined; if (startOrEnd == null) return undefined;
const { value: mode } = await getSwal().Swal.fire<FindKeyframeMode | 'opposing'>({ const { value: mode } = await getSwal().Swal.fire<FindKeyframeMode>({
input: 'radio', input: 'radio',
showCancelButton: true, showCancelButton: true,
inputOptions: { inputOptions: {
nearest: i18n.t('Nearest keyframe'), nearest: i18n.t('Nearest keyframe'),
before: i18n.t('Previous keyframe'), before: i18n.t('Previous keyframe'),
after: i18n.t('Next keyframe'), after: i18n.t('Next keyframe'),
opposing: i18n.t('Segment start to previous keyframe and end to next keyframe'), keyframeCutFix: i18n.t('True keyframe cut'),
} satisfies Record<FindKeyframeMode | 'opposing', unknown>, } satisfies Record<FindKeyframeMode, unknown>,
inputValue: 'before', inputValue: 'keyframeCutFix',
text: i18n.t('Do you want to align segment times to the nearest, previous or next keyframe?'), text: i18n.t('Do you want to align segment times to the nearest, previous or next keyframe?'),
}); });

@ -108,7 +108,7 @@ export const findNextKeyframe = (keyframes: Frame[], time: number) => keyframes.
const findPreviousKeyframe = (keyframes: Frame[], time: number) => keyframes.findLast((keyframe) => keyframe.time <= time); const findPreviousKeyframe = (keyframes: Frame[], time: number) => keyframes.findLast((keyframe) => keyframe.time <= time);
const findNearestKeyframe = (keyframes: Frame[], time: number) => minBy(keyframes, (keyframe) => Math.abs(keyframe.time - time)); const findNearestKeyframe = (keyframes: Frame[], time: number) => minBy(keyframes, (keyframe) => Math.abs(keyframe.time - time));
export type FindKeyframeMode = 'nearest' | 'before' | 'after'; export type FindKeyframeMode = 'nearest' | 'before' | 'after' | 'keyframeCutFix';
function findKeyframe(keyframes: Frame[], time: number, mode: FindKeyframeMode) { function findKeyframe(keyframes: Frame[], time: number, mode: FindKeyframeMode) {
switch (mode) { switch (mode) {
@ -121,6 +121,9 @@ function findKeyframe(keyframes: Frame[], time: number, mode: FindKeyframeMode)
case 'after': { case 'after': {
return findNextKeyframe(keyframes, time); return findNextKeyframe(keyframes, time);
} }
case 'keyframeCutFix': {
return findNextKeyframe(keyframes,time);
}
default: { default: {
return undefined; return undefined;
} }

@ -8,8 +8,7 @@ import sortBy from 'lodash/sortBy';
import { Trans, useTranslation } from 'react-i18next'; import { Trans, useTranslation } from 'react-i18next';
import { FaLink } from 'react-icons/fa'; import { FaLink } from 'react-icons/fa';
import TextInput from '../components/TextInput'; import { detectSceneChanges as ffmpegDetectSceneChanges, readFrames, mapTimesToSegments, findKeyframeNearTime, getStreamFps } from '../ffmpeg';
import { detectSceneChanges as ffmpegDetectSceneChanges, readFrames, mapTimesToSegments, findKeyframeNearTime } from '../ffmpeg';
import { getFileSize, shuffleArray } from '../util'; import { getFileSize, shuffleArray } from '../util';
import { errorToast } from '../swal'; import { errorToast } from '../swal';
import { createNumSegments as createNumSegmentsDialog, createFixedByteSixedSegments as createFixedByteSixedSegmentsDialog, createRandomSegments as createRandomSegmentsDialog, labelSegmentDialog, askForShiftSegments, askForAlignSegments, selectSegmentsByLabelDialog, askForSegmentDuration, toastError } from '../dialogs'; import { createNumSegments as createNumSegmentsDialog, createFixedByteSixedSegments as createFixedByteSixedSegmentsDialog, createRandomSegments as createRandomSegmentsDialog, labelSegmentDialog, askForShiftSegments, askForAlignSegments, selectSegmentsByLabelDialog, askForSegmentDuration, toastError } from '../dialogs';
@ -516,25 +515,50 @@ function useSegments({ filePath, workingRef, setWorking, setProgress, videoStrea
if (response == null) return; if (response == null) return;
setWorking({ text: i18n.t('Aligning segments to keyframes') }); setWorking({ text: i18n.t('Aligning segments to keyframes') });
const { mode, startOrEnd } = response; const { mode, startOrEnd } = response;
if (filePath == null) throw new Error();
const frameTime = 1 / (getStreamFps(videoStream) || 1000);
await modifySelectedSegmentTimes(async (segment) => { await modifySelectedSegmentTimes(async (segment) => {
const newSegment = { ...segment }; const newSegment = { ...segment };
const align = async (key: 'start' | 'end') => { async function align(key: string) {
const time = newSegment[key]; const time = newSegment[key];
invariant(filePath != null); invariant(filePath != null);
if (time != null) { if (time != null) {
const keyframe = await findKeyframeNearTime({ let keyframe = await findKeyframeNearTime({ filePath, streamIndex: videoStream.index, time, mode });
filePath, if (keyframe === undefined) {
streamIndex: videoStream.index, if (mode !== 'keyframeCutFix') {
time, throw new UserFacingError(i18n.t('Cannot find any keyframe within 60 seconds of frame {{time}}', { time }));
mode: mode === 'opposing' ? (key === 'start' ? 'before' : 'after') : mode, }
}); keyframe = fileDuration;
if (keyframe == null) throw new UserFacingError(i18n.t('Cannot find any keyframe within 60 seconds of frame {{time}}', { time })); console.log(2,time,keyframe);
}
newSegment[key] = keyframe; newSegment[key] = keyframe;
} }
}; }
if (startOrEnd.includes('start')) await align('start'); if (startOrEnd.includes('start')) {
if (startOrEnd.includes('end')) await align('end'); if (mode === 'keyframeCutFix') {
newSegment.start += frameTime * 0.3;
}
await align('start');
if (mode === 'keyframeCutFix') {
newSegment.start -= frameTime * 0.7;
}
}
if (startOrEnd.includes('end')) {
await align('end');
if (mode === 'keyframeCutFix' && newSegment.end !== fileDuration) {
newSegment.end -= frameTime * 0.3;
}
}
if (startOrEnd.includes('start')) {
newSegment.start = Math.min(newSegment.start, newSegment.end - frameTime * 0.99); // don't know how ffmpeg interprets cuts between frames
} else {
newSegment.end = Math.max(newSegment.start + frameTime * 0.99, newSegment.end);
}
return newSegment; return newSegment;
}); });
} catch (err) { } catch (err) {
@ -542,7 +566,7 @@ function useSegments({ filePath, workingRef, setWorking, setProgress, videoStrea
} finally { } finally {
setWorking(undefined); setWorking(undefined);
} }
}, [videoStream, workingRef, setWorking, modifySelectedSegmentTimes, filePath, handleError]); }, [filePath, videoStream, modifySelectedSegmentTimes, setWorking, workingRef, fileDuration, handleError]);
const updateSegOrder = useCallback((index: number, newOrder: number) => { const updateSegOrder = useCallback((index: number, newOrder: number) => {
if (newOrder > cutSegments.length - 1 || newOrder < 0) return; if (newOrder > cutSegments.length - 1 || newOrder < 0) return;

Loading…
Cancel
Save