mirror of
https://github.com/SubtitleEdit/subtitleedit.git
synced 2024-11-26 05:02:36 +01:00
Fix exceptions for edgecase with overlapping subtitles or a large "Treat as connected" setting
This commit is contained in:
parent
86e033ee62
commit
c2dd129b20
@ -425,7 +425,7 @@ namespace Test.Logic
|
||||
[TestMethod]
|
||||
public void TestFirstWithin()
|
||||
{
|
||||
var shotChangesFrames = new List<int>() { 1000, 10000, 20000 };
|
||||
var shotChangesFrames = new List<int>() { 50, 1000, 3000, 5000, 10000, 11000, 14000, 15000, 16000, 20000, 22000 };
|
||||
|
||||
int? First(int start, int end)
|
||||
{
|
||||
@ -455,6 +455,47 @@ namespace Test.Logic
|
||||
Assert.AreEqual(First(20001, 20001), shotChangesFrames.FirstWithin(20001, 20001));
|
||||
Assert.AreEqual(First(30000, 30000), shotChangesFrames.FirstWithin(30000, 30000));
|
||||
Assert.AreEqual(First(30000, 40000), shotChangesFrames.FirstWithin(30000, 40000));
|
||||
Assert.AreEqual(First(25000, 5000), shotChangesFrames.FirstWithin(25000, 5000));
|
||||
Assert.AreEqual(First(50, 60), shotChangesFrames.FirstWithin(50, 60));
|
||||
Assert.AreEqual(First(50, 1100), shotChangesFrames.FirstWithin(50, 1100));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestLastWithin()
|
||||
{
|
||||
var shotChangesFrames = new List<int>() { 50, 1000, 3000, 5000, 10000, 11000, 14000, 15000, 16000, 20000, 22000 };
|
||||
|
||||
int? Last(int start, int end)
|
||||
{
|
||||
try
|
||||
{
|
||||
return shotChangesFrames.Last(x => x >= start && x <= end);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.AreEqual(Last(-100, 100000), shotChangesFrames.LastWithin(-100, 100000));
|
||||
Assert.AreEqual(Last(0, 100), shotChangesFrames.LastWithin(0, 100));
|
||||
Assert.AreEqual(Last(0, 1000), shotChangesFrames.LastWithin(0, 1000));
|
||||
Assert.AreEqual(Last(1000, 1000), shotChangesFrames.LastWithin(1000, 1000));
|
||||
Assert.AreEqual(Last(1000, 1100), shotChangesFrames.LastWithin(1000, 1100));
|
||||
Assert.AreEqual(Last(1100, 900), shotChangesFrames.LastWithin(1100, 900));
|
||||
Assert.AreEqual(Last(900, 15000), shotChangesFrames.LastWithin(900, 15000));
|
||||
Assert.AreEqual(Last(900, 30000), shotChangesFrames.LastWithin(900, 30000));
|
||||
Assert.AreEqual(Last(19999, 19999), shotChangesFrames.LastWithin(19999, 19999));
|
||||
Assert.AreEqual(Last(19999, 20000), shotChangesFrames.LastWithin(19999, 20000));
|
||||
Assert.AreEqual(Last(20000, 20000), shotChangesFrames.LastWithin(20000, 20000));
|
||||
Assert.AreEqual(Last(20000, 20001), shotChangesFrames.LastWithin(20000, 20001));
|
||||
Assert.AreEqual(Last(19999, 20001), shotChangesFrames.LastWithin(19999, 20001));
|
||||
Assert.AreEqual(Last(20001, 20001), shotChangesFrames.LastWithin(20001, 20001));
|
||||
Assert.AreEqual(Last(30000, 30000), shotChangesFrames.LastWithin(30000, 30000));
|
||||
Assert.AreEqual(Last(30000, 40000), shotChangesFrames.LastWithin(30000, 40000));
|
||||
Assert.AreEqual(Last(25000, 5000), shotChangesFrames.LastWithin(25000, 5000));
|
||||
Assert.AreEqual(Last(50, 60), shotChangesFrames.LastWithin(50, 60));
|
||||
Assert.AreEqual(Last(50, 1100), shotChangesFrames.LastWithin(50, 1100));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ namespace Nikse.SubtitleEdit.Core.Common
|
||||
}
|
||||
|
||||
|
||||
// First within (int)
|
||||
// First / last within (int)
|
||||
|
||||
public static int? FirstWithin(this List<int> orderedList, int start, int end)
|
||||
{
|
||||
@ -177,5 +177,28 @@ namespace Nikse.SubtitleEdit.Core.Common
|
||||
|
||||
return orderedList[startIndex];
|
||||
}
|
||||
|
||||
public static int? LastWithin(this List<int> orderedList, int start, int end)
|
||||
{
|
||||
int startIndex = orderedList.BinarySearch(start);
|
||||
int endIndex = orderedList.BinarySearch(end);
|
||||
|
||||
if (startIndex < 0)
|
||||
{
|
||||
startIndex = ~startIndex;
|
||||
}
|
||||
|
||||
if (endIndex < 0)
|
||||
{
|
||||
endIndex = ~endIndex - 1;
|
||||
}
|
||||
|
||||
if (startIndex > endIndex || startIndex >= orderedList.Count || orderedList[startIndex] > end || endIndex >= orderedList.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return orderedList[endIndex];
|
||||
}
|
||||
}
|
||||
}
|
@ -133,14 +133,14 @@ namespace Nikse.SubtitleEdit.Core.Forms
|
||||
var leftInCueFrame = MillisecondsToFrames(leftParagraph.StartTime.TotalMilliseconds);
|
||||
var rightOutCueFrame = MillisecondsToFrames(rightParagraph.EndTime.TotalMilliseconds);
|
||||
|
||||
// Check result
|
||||
if (bestLeftOutCueFrameInfo.result == FindBestCueResult.SnappedToRedZone && bestRightInCueFrameInfo.result == FindBestCueResult.SnappedToRedZone)
|
||||
// Define align function for reusing
|
||||
void AlignCuesAroundClosestShotChange(int leftShotChangeFrame, int rightShotChangeFrame)
|
||||
{
|
||||
var fixInfoForLeft = GetFixedConnectedSubtitlesCueFrames(leftParagraph, rightParagraph, bestLeftOutCueFrameInfo.cueFrame);
|
||||
var fixInfoForRight = GetFixedConnectedSubtitlesCueFrames(leftParagraph, rightParagraph, bestRightInCueFrameInfo.cueFrame);
|
||||
var fixInfoForLeft = GetFixedConnectedSubtitlesCueFrames(leftParagraph, rightParagraph, leftShotChangeFrame);
|
||||
var fixInfoForRight = GetFixedConnectedSubtitlesCueFrames(leftParagraph, rightParagraph, rightShotChangeFrame);
|
||||
|
||||
// Both are in red zones! We will use the closest shot change to align the cues around
|
||||
if (Math.Abs(newLeftOutCueFrame - bestLeftOutCueFrameInfo.cueFrame) <= Math.Abs(newRightInCueFrame - bestRightInCueFrameInfo.cueFrame))
|
||||
// Calculate which shot change is closer
|
||||
if (Math.Abs(newLeftOutCueFrame - leftShotChangeFrame) <= Math.Abs(newRightInCueFrame - rightShotChangeFrame))
|
||||
{
|
||||
// Align around the left shot change
|
||||
// Except, when the left subtitle now becomes invalid (negative duration) and the right subtitle won't, we will use the right shot change anyway
|
||||
@ -175,6 +175,13 @@ namespace Nikse.SubtitleEdit.Core.Forms
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check result
|
||||
if (bestLeftOutCueFrameInfo.result == FindBestCueResult.SnappedToRedZone && bestRightInCueFrameInfo.result == FindBestCueResult.SnappedToRedZone)
|
||||
{
|
||||
// Both are in red zones! We will use the closest shot change to align the cues around
|
||||
AlignCuesAroundClosestShotChange(bestLeftOutCueFrameInfo.cueFrame, bestRightInCueFrameInfo.cueFrame);
|
||||
}
|
||||
else if ((bestLeftOutCueFrameInfo.result == FindBestCueResult.SnappedToLeftGreenZone || bestLeftOutCueFrameInfo.result == FindBestCueResult.SnappedToRightGreenZone) &&
|
||||
(bestRightInCueFrameInfo.result == FindBestCueResult.SnappedToLeftGreenZone || bestRightInCueFrameInfo.result == FindBestCueResult.SnappedToRightGreenZone))
|
||||
{
|
||||
@ -280,7 +287,24 @@ namespace Nikse.SubtitleEdit.Core.Forms
|
||||
}
|
||||
else if (bestLeftOutCueFrameInfo.result == FindBestCueResult.SnappedToLeftGreenZone)
|
||||
{
|
||||
throw new InvalidOperationException("The left out cue cannot be snapped to the left side of a green zone while the right in cue is unaffected at the same time.");
|
||||
// The left out cue wants to go backward, while the right in cue requires no action... The "Treat as connected" setting is probably really high.
|
||||
// We'll use this function in case there are any shot changes closer to the right in cue.
|
||||
var lastShotChangeInBetween = GetLastShotChangeFrameInBetween(bestLeftOutCueFrameInfo.cueFrame, bestRightInCueFrameInfo.cueFrame);
|
||||
if (lastShotChangeInBetween != null)
|
||||
{
|
||||
// Derive left out cue shot change
|
||||
var leftOutCueShotChange = bestLeftOutCueFrameInfo.cueFrame + Configuration.Settings.BeautifyTimeCodes.Profile.ConnectedSubtitlesLeftGreenZone;
|
||||
|
||||
// We will use the closest shot change to align the cues around
|
||||
AlignCuesAroundClosestShotChange(leftOutCueShotChange, lastShotChangeInBetween.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// There are no shot changes at all between the two cues! That likely means they are overlapping.
|
||||
// Since the left out cue requires a change, we'll accommodate that: put the right in cue on the edge of the green zone, and push the previous subtitle backward.
|
||||
newRightInCueFrame = bestLeftOutCueFrameInfo.cueFrame;
|
||||
newLeftOutCueFrame = newRightInCueFrame - Configuration.Settings.BeautifyTimeCodes.Profile.Gap;
|
||||
}
|
||||
}
|
||||
else if (bestLeftOutCueFrameInfo.result == FindBestCueResult.SnappedToRightGreenZone)
|
||||
{
|
||||
@ -296,7 +320,24 @@ namespace Nikse.SubtitleEdit.Core.Forms
|
||||
}
|
||||
else if (bestRightInCueFrameInfo.result == FindBestCueResult.SnappedToRightGreenZone)
|
||||
{
|
||||
throw new InvalidOperationException("The right in cue cannot be snapped to the right side of a green zone while the left out cue is unaffected at the same time.");
|
||||
// The right in cue wants to go forward, while the left out cue requires no action... The "Treat as connected" setting is probably really high.
|
||||
// We'll use this function in case there are any shot changes closer to the left out cue.
|
||||
var firstShotChangeInBetween = GetFirstShotChangeFrameInBetween(bestLeftOutCueFrameInfo.cueFrame, bestRightInCueFrameInfo.cueFrame);
|
||||
if (firstShotChangeInBetween != null)
|
||||
{
|
||||
// Derive right in cue shot change
|
||||
var rightInCueShotChange = bestRightInCueFrameInfo.cueFrame - Configuration.Settings.BeautifyTimeCodes.Profile.ConnectedSubtitlesRightGreenZone;
|
||||
|
||||
// We will use the closest shot change to align the cues around
|
||||
AlignCuesAroundClosestShotChange(firstShotChangeInBetween.Value, rightInCueShotChange);
|
||||
}
|
||||
else
|
||||
{
|
||||
// There are no shot changes at all between the two cues! That likely means they are overlapping.
|
||||
// Since the right in cue requires a change, we'll accommodate that: put the left out cue on the edge of the green zone, and push the next subtitle forward.
|
||||
newLeftOutCueFrame = bestRightInCueFrameInfo.cueFrame;
|
||||
newRightInCueFrame = newLeftOutCueFrame + Configuration.Settings.BeautifyTimeCodes.Profile.Gap;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -939,6 +980,16 @@ namespace Nikse.SubtitleEdit.Core.Forms
|
||||
return _shotChangesFrames.FirstWithin(leftCueFrame, rightCueFrame);
|
||||
}
|
||||
|
||||
private int? GetLastShotChangeFrameInBetween(int leftCueFrame, int rightCueFrame)
|
||||
{
|
||||
if (_shotChangesFrames == null || _shotChangesFrames.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _shotChangesFrames.LastWithin(leftCueFrame, rightCueFrame);
|
||||
}
|
||||
|
||||
private int? GetClosestShotChangeFrame(int cueFrame)
|
||||
{
|
||||
if (_shotChangesFrames == null || _shotChangesFrames.Count == 0)
|
||||
|
Loading…
Reference in New Issue
Block a user