SubtitleEdit/libse/Subtitle.cs

662 lines
26 KiB
C#
Raw Normal View History

2016-02-08 21:11:03 +01:00
using Nikse.SubtitleEdit.Core.Enums;
using Nikse.SubtitleEdit.Core.SubtitleFormats;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Nikse.SubtitleEdit.Core
{
public class Subtitle
{
public const int MaxFileSize = 1024 * 1024 * 20; // 20 MB
2016-02-08 21:11:03 +01:00
private List<Paragraph> _paragraphs;
private readonly List<HistoryItem> _history;
2017-03-14 12:08:54 +01:00
public string Header { get; set; } = string.Empty;
public string Footer { get; set; } = string.Empty;
2016-02-08 21:11:03 +01:00
public string FileName { get; set; }
public static int MaximumHistoryItems => 100;
2016-02-08 21:11:03 +01:00
2018-01-27 13:50:44 +01:00
public SubtitleFormat OriginalFormat { get; private set; }
2016-02-08 21:11:03 +01:00
2018-01-27 13:50:44 +01:00
public List<HistoryItem> HistoryItems => _history;
2016-02-08 21:11:03 +01:00
public Subtitle()
{
_paragraphs = new List<Paragraph>();
_history = new List<HistoryItem>();
FileName = "Untitled";
}
public Subtitle(List<HistoryItem> historyItems)
: this()
{
_history = historyItems;
}
public Subtitle(List<Paragraph> paragraphs, List<HistoryItem> historyItems)
2019-06-04 09:27:51 +02:00
: this()
{
_history = historyItems;
_paragraphs = paragraphs;
}
2016-02-08 21:11:03 +01:00
/// <summary>
/// Copy constructor (only paragraphs)
/// </summary>
/// <param name="subtitle">Subtitle to copy</param>
/// <param name="generateNewId">Generate new ID (guid) for paragraphs</param>
public Subtitle(Subtitle subtitle, bool generateNewId = true)
: this()
{
if (subtitle == null)
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
return;
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
foreach (Paragraph p in subtitle.Paragraphs)
{
_paragraphs.Add(new Paragraph(p, generateNewId));
}
Header = subtitle.Header;
Footer = subtitle.Footer;
FileName = subtitle.FileName;
2016-02-08 21:11:03 +01:00
}
public Subtitle(List<Paragraph> paragraphs) : this()
{
_paragraphs = paragraphs;
}
2018-01-27 13:50:44 +01:00
public List<Paragraph> Paragraphs => _paragraphs;
2016-02-08 21:11:03 +01:00
/// <summary>
/// Get the paragraph of index, null if out of bounds
/// </summary>
/// <param name="index">Index of wanted paragraph</param>
/// <returns>Paragraph, null if index is index is out of bounds</returns>
public Paragraph GetParagraphOrDefault(int index)
{
if (_paragraphs == null || _paragraphs.Count <= index || index < 0)
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
return null;
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
return _paragraphs[index];
}
public Paragraph GetParagraphOrDefaultById(string id)
{
2019-09-29 14:33:26 +02:00
return _paragraphs.FirstOrDefault(p => p.Id == id);
2016-02-08 21:11:03 +01:00
}
public SubtitleFormat ReloadLoadSubtitle(List<string> lines, string fileName, SubtitleFormat format)
{
Paragraphs.Clear();
if (format != null && format.IsMine(lines, fileName))
{
format.LoadSubtitle(this, lines, fileName);
2018-01-27 13:50:44 +01:00
OriginalFormat = format;
2016-02-08 21:11:03 +01:00
return format;
}
foreach (SubtitleFormat subtitleFormat in SubtitleFormat.AllSubtitleFormats)
{
if (subtitleFormat.IsMine(lines, fileName))
{
subtitleFormat.LoadSubtitle(this, lines, fileName);
2018-01-27 13:50:44 +01:00
OriginalFormat = subtitleFormat;
2016-02-08 21:11:03 +01:00
return subtitleFormat;
}
}
return null;
}
public SubtitleFormat LoadSubtitle(string fileName, out Encoding encoding, Encoding useThisEncoding)
{
return LoadSubtitle(fileName, out encoding, useThisEncoding, false);
}
2019-08-25 19:26:50 +02:00
public SubtitleFormat LoadSubtitle(string fileName, out Encoding encoding, Encoding useThisEncoding, bool batchMode, double? sourceFrameRate = null, bool loadSubtitle = true)
2016-02-08 21:11:03 +01:00
{
FileName = fileName;
_paragraphs = new List<Paragraph>();
StreamReader sr;
if (useThisEncoding != null)
{
try
{
sr = new StreamReader(fileName, useThisEncoding);
}
catch (Exception exception)
{
System.Diagnostics.Debug.WriteLine(exception.Message);
encoding = Encoding.UTF8;
return null;
}
}
else
{
try
{
sr = new StreamReader(fileName, LanguageAutoDetect.GetEncodingFromFile(fileName), true);
}
catch
{
try
{
var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
2016-02-08 21:11:03 +01:00
sr = new StreamReader(fs);
}
catch (Exception exception)
{
System.Diagnostics.Debug.WriteLine(exception.Message);
encoding = Encoding.UTF8;
return null;
}
}
}
encoding = sr.CurrentEncoding;
var lines = sr.ReadToEnd().SplitToLines();
2016-02-08 21:11:03 +01:00
sr.Close();
var ext = Path.GetExtension(fileName).ToLowerInvariant();
foreach (var subtitleFormat in SubtitleFormat.AllSubtitleFormats.Where(p => p.Extension == ext && !p.Name.StartsWith("Unknown", StringComparison.Ordinal)))
2016-02-08 21:11:03 +01:00
{
if (subtitleFormat.IsMine(lines, fileName))
{
2019-08-25 19:26:50 +02:00
return FinalizeFormat(fileName, batchMode, sourceFrameRate, lines, subtitleFormat, loadSubtitle);
}
}
foreach (var subtitleFormat in SubtitleFormat.AllSubtitleFormats.Where(p => p.Extension != ext || p.Name.StartsWith("Unknown", StringComparison.Ordinal)))
{
if (subtitleFormat.IsMine(lines, fileName))
{
2019-08-25 19:26:50 +02:00
return FinalizeFormat(fileName, batchMode, sourceFrameRate, lines, subtitleFormat, loadSubtitle);
2016-02-08 21:11:03 +01:00
}
}
if (useThisEncoding == null)
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
return LoadSubtitle(fileName, out encoding, Encoding.Unicode);
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
return null;
}
2019-08-25 19:26:50 +02:00
private SubtitleFormat FinalizeFormat(string fileName, bool batchMode, double? sourceFrameRate, List<string> lines, SubtitleFormat subtitleFormat, bool loadSubtitle)
{
Header = null;
subtitleFormat.BatchMode = batchMode;
subtitleFormat.BatchSourceFrameRate = sourceFrameRate;
2019-08-25 19:26:50 +02:00
if (loadSubtitle)
{
subtitleFormat.LoadSubtitle(this, lines, fileName);
}
OriginalFormat = subtitleFormat;
return subtitleFormat;
}
2016-02-08 21:11:03 +01:00
public void MakeHistoryForUndo(string description, SubtitleFormat subtitleFormat, DateTime fileModified, Subtitle original, string originalSubtitleFileName, int lineNumber, int linePosition, int linePositionAlternate)
{
// don't fill memory with history - use a max rollback points
if (_history.Count > MaximumHistoryItems)
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
_history.RemoveAt(0);
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
_history.Add(new HistoryItem(_history.Count, this, description, FileName, fileModified, subtitleFormat.FriendlyName, original, originalSubtitleFileName, lineNumber, linePosition, linePositionAlternate));
}
2018-01-27 13:50:44 +01:00
public bool CanUndo => _history.Count > 0;
2016-02-08 21:11:03 +01:00
public string UndoHistory(int index, out string subtitleFormatFriendlyName, out DateTime fileModified, out Subtitle originalSubtitle, out string originalSubtitleFileName)
{
_paragraphs.Clear();
foreach (Paragraph p in _history[index].Subtitle.Paragraphs)
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
_paragraphs.Add(new Paragraph(p));
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
subtitleFormatFriendlyName = _history[index].SubtitleFormatFriendlyName;
FileName = _history[index].FileName;
fileModified = _history[index].FileModified;
originalSubtitle = new Subtitle(_history[index].OriginalSubtitle);
originalSubtitleFileName = _history[index].OriginalSubtitleFileName;
2017-09-06 16:51:15 +02:00
Header = _history[index].Subtitle.Header;
2016-02-08 21:11:03 +01:00
return FileName;
}
/// <summary>
2016-09-07 04:38:47 +02:00
/// Creates subtitle as text in its native format.
2016-02-08 21:11:03 +01:00
/// </summary>
/// <param name="format">Format to output</param>
/// <returns>Native format as text string</returns>
public string ToText(SubtitleFormat format)
{
return format.ToText(this, Path.GetFileNameWithoutExtension(FileName));
}
public void AddTimeToAllParagraphs(TimeSpan time)
{
2017-05-05 18:20:57 +02:00
double milliseconds = time.TotalMilliseconds;
2016-02-08 21:11:03 +01:00
foreach (Paragraph p in Paragraphs)
{
2017-05-05 18:20:57 +02:00
p.StartTime.TotalMilliseconds += milliseconds;
p.EndTime.TotalMilliseconds += milliseconds;
2016-02-08 21:11:03 +01:00
}
}
public void ChangeFrameRate(double oldFrameRate, double newFrameRate)
{
foreach (Paragraph p in Paragraphs)
{
p.StartTime.TotalMilliseconds = (p.StartTime.TotalMilliseconds * oldFrameRate / newFrameRate);
p.EndTime.TotalMilliseconds = (p.EndTime.TotalMilliseconds * oldFrameRate / newFrameRate);
}
}
public void AdjustDisplayTimeUsingPercent(double percent, List<int> selectedIndexes)
{
for (int i = 0; i < _paragraphs.Count; i++)
{
if (selectedIndexes == null || selectedIndexes.Contains(i))
{
double nextStartMilliseconds = double.MaxValue;
2016-02-08 21:11:03 +01:00
if (i + 1 < _paragraphs.Count)
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
nextStartMilliseconds = _paragraphs[i + 1].StartTime.TotalMilliseconds;
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
double newEndMilliseconds = _paragraphs[i].EndTime.TotalMilliseconds;
newEndMilliseconds = _paragraphs[i].StartTime.TotalMilliseconds + (((newEndMilliseconds - _paragraphs[i].StartTime.TotalMilliseconds) * percent) / 100.0);
2016-02-08 21:11:03 +01:00
if (newEndMilliseconds > nextStartMilliseconds)
2019-01-19 14:40:37 +01:00
{
newEndMilliseconds = nextStartMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines;
2019-01-19 14:40:37 +01:00
}
if (percent > 100 && newEndMilliseconds > _paragraphs[i].EndTime.TotalMilliseconds || percent < 100)
2019-01-19 14:40:37 +01:00
{
_paragraphs[i].EndTime.TotalMilliseconds = newEndMilliseconds;
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
}
}
}
public void AdjustDisplayTimeUsingSeconds(double seconds, List<int> selectedIndexes)
{
for (int i = 0; i < _paragraphs.Count; i++)
{
if (selectedIndexes == null || selectedIndexes.Contains(i))
{
double nextStartMilliseconds = double.MaxValue;
2016-02-08 21:11:03 +01:00
if (i + 1 < _paragraphs.Count)
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
nextStartMilliseconds = _paragraphs[i + 1].StartTime.TotalMilliseconds;
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
double newEndMilliseconds = _paragraphs[i].EndTime.TotalMilliseconds + (seconds * TimeCode.BaseUnit);
if (newEndMilliseconds > nextStartMilliseconds)
2019-01-19 14:40:37 +01:00
{
newEndMilliseconds = nextStartMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines;
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
if (seconds < 0)
{
if (_paragraphs[i].StartTime.TotalMilliseconds + 100 > newEndMilliseconds)
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
_paragraphs[i].EndTime.TotalMilliseconds = _paragraphs[i].StartTime.TotalMilliseconds + 100;
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
else
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
_paragraphs[i].EndTime.TotalMilliseconds = newEndMilliseconds;
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
}
else if (newEndMilliseconds > _paragraphs[i].EndTime.TotalMilliseconds)
2016-02-08 21:11:03 +01:00
{
_paragraphs[i].EndTime.TotalMilliseconds = newEndMilliseconds;
}
}
}
}
public void RecalculateDisplayTimes(double maxCharactersPerSecond, List<int> selectedIndexes, double optimalCharactersPerSeconds, bool extendOnly = false)
2016-02-08 21:11:03 +01:00
{
for (int i = 0; i < _paragraphs.Count; i++)
{
if (selectedIndexes == null || selectedIndexes.Contains(i))
{
RecalculateDisplayTime(maxCharactersPerSecond, i, optimalCharactersPerSeconds, extendOnly);
2016-02-08 21:11:03 +01:00
}
}
}
public void RecalculateDisplayTime(double maxCharactersPerSecond, int index, double optimalCharactersPerSeconds, bool extendOnly = false)
{
Paragraph p = GetParagraphOrDefault(index);
if (p == null)
2019-01-19 14:40:37 +01:00
{
return;
2019-01-19 14:40:37 +01:00
}
double originalEndTime = p.EndTime.TotalMilliseconds;
double duration = Utilities.GetOptimalDisplayMilliseconds(p.Text, optimalCharactersPerSeconds);
p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + duration;
2019-01-29 21:33:20 +01:00
var numberOfCharacters = p.Text.CountCharacters(Configuration.Settings.General.CharactersPerSecondsIgnoreWhiteSpace);
while (Utilities.GetCharactersPerSecond(p, numberOfCharacters) > maxCharactersPerSecond)
{
duration++;
p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + duration;
}
if (extendOnly && p.EndTime.TotalMilliseconds < originalEndTime)
{
p.EndTime.TotalMilliseconds = originalEndTime;
}
Paragraph next = GetParagraphOrDefault(index + 1);
if (next != null && p.StartTime.TotalMilliseconds + duration + Configuration.Settings.General.MinimumMillisecondsBetweenLines > next.StartTime.TotalMilliseconds)
{
p.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines;
if (p.Duration.TotalMilliseconds <= 0)
2019-01-19 14:40:37 +01:00
{
p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + 1;
2019-01-19 14:40:37 +01:00
}
}
}
2019-01-29 21:33:20 +01:00
public void SetFixedDuration(List<int> selectedIndexes, double fixedDurationMilliseconds)
{
for (int i = 0; i < _paragraphs.Count; i++)
{
if (selectedIndexes == null || selectedIndexes.Contains(i))
{
Paragraph p = GetParagraphOrDefault(i);
if (p == null)
2019-01-19 14:40:37 +01:00
{
continue;
2019-01-19 14:40:37 +01:00
}
p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + fixedDurationMilliseconds;
Paragraph next = GetParagraphOrDefault(i + 1);
if (next != null && p.StartTime.TotalMilliseconds + fixedDurationMilliseconds + Configuration.Settings.General.MinimumMillisecondsBetweenLines > next.StartTime.TotalMilliseconds)
{
p.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines;
if (p.Duration.TotalMilliseconds <= 0)
2019-01-19 14:40:37 +01:00
{
p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + 1;
2019-01-19 14:40:37 +01:00
}
}
}
}
}
2016-02-08 21:11:03 +01:00
public void Renumber(int startNumber = 1)
{
var number = startNumber;
2019-02-23 09:48:12 +01:00
int l = _paragraphs.Count + number;
while (number < l)
2016-02-08 21:11:03 +01:00
{
2019-02-23 09:48:12 +01:00
var p = _paragraphs[number - startNumber];
p.Number = number++;
2016-02-08 21:11:03 +01:00
}
}
public int GetIndex(Paragraph p)
{
if (p == null)
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
return -1;
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
int index = _paragraphs.IndexOf(p);
if (index >= 0)
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
return index;
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
for (int i = 0; i < _paragraphs.Count; i++)
{
2019-09-29 14:33:26 +02:00
if (p.Id == _paragraphs[i].Id)
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
return i;
2019-01-19 14:40:37 +01:00
}
2019-09-29 14:33:26 +02:00
if (i < _paragraphs.Count - 1 && p.Id == _paragraphs[i + 1].Id)
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
return i + 1;
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
if (Math.Abs(p.StartTime.TotalMilliseconds - _paragraphs[i].StartTime.TotalMilliseconds) < 0.1 &&
Math.Abs(p.EndTime.TotalMilliseconds - _paragraphs[i].EndTime.TotalMilliseconds) < 0.1)
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
return i;
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
if (p.Number == _paragraphs[i].Number && (Math.Abs(p.StartTime.TotalMilliseconds - _paragraphs[i].StartTime.TotalMilliseconds) < 0.1 ||
Math.Abs(p.EndTime.TotalMilliseconds - _paragraphs[i].EndTime.TotalMilliseconds) < 0.1))
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
return i;
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
if (p.Text == _paragraphs[i].Text && (Math.Abs(p.StartTime.TotalMilliseconds - _paragraphs[i].StartTime.TotalMilliseconds) < 0.1 ||
Math.Abs(p.EndTime.TotalMilliseconds - _paragraphs[i].EndTime.TotalMilliseconds) < 0.1))
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
return i;
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
}
return -1;
}
/// <summary>
/// Get paragraph index by time in seconds
/// </summary>
public int GetIndex(double seconds)
{
var totalMilliseconds = seconds * TimeCode.BaseUnit;
for (int i = 0; i < Paragraphs.Count; i++)
{
var p = Paragraphs[i];
if (totalMilliseconds >= p.StartTime.TotalMilliseconds && totalMilliseconds <= p.EndTime.TotalMilliseconds)
{
return i;
}
}
return -1;
}
public Paragraph GetFirstAlike(Paragraph p)
{
foreach (Paragraph item in _paragraphs)
{
if (Math.Abs(p.StartTime.TotalMilliseconds - item.StartTime.TotalMilliseconds) < 0.1 &&
Math.Abs(p.EndTime.TotalMilliseconds - item.EndTime.TotalMilliseconds) < 0.1 &&
p.Text == item.Text)
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
return item;
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
}
return null;
}
public int RemoveEmptyLines()
{
int count = _paragraphs.Count;
if (count > 0)
{
int firstNumber = _paragraphs[0].Number;
for (int i = _paragraphs.Count - 1; i >= 0; i--)
{
Paragraph p = _paragraphs[i];
if (string.IsNullOrWhiteSpace(p.Text.RemoveControlCharacters()))
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
_paragraphs.RemoveAt(i);
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
}
if (count != _paragraphs.Count)
2019-01-19 14:40:37 +01:00
{
2016-02-08 21:11:03 +01:00
Renumber(firstNumber);
2019-01-19 14:40:37 +01:00
}
2016-02-08 21:11:03 +01:00
}
return count - _paragraphs.Count;
}
/// <summary>
/// Removes paragrahs by a list of indices
/// </summary>
/// <param name="indices">Indices of pargraphs/lines to delete</param>
/// <returns>Number of lines deleted</returns>
public int RemoveParagraphsByIndices(IEnumerable<int> indices)
{
int count = 0;
foreach (var index in indices.OrderByDescending(p => p))
{
if (index >= 0 && index < _paragraphs.Count)
{
_paragraphs.RemoveAt(index);
count++;
}
}
return count;
}
/// <summary>
/// Removes paragrahs by a list of IDs
/// </summary>
/// <param name="ids">IDs of pargraphs/lines to delete</param>
/// <returns>Number of lines deleted</returns>
public int RemoveParagraphsByIds(IEnumerable<string> ids)
{
int beforeCount = _paragraphs.Count;
2019-09-29 14:33:26 +02:00
_paragraphs = _paragraphs.Where(p => !ids.Contains(p.Id)).ToList();
2016-02-08 21:11:03 +01:00
return beforeCount - _paragraphs.Count;
}
/// <summary>
/// Sort subtitle paragraphs
/// </summary>
/// <param name="sortCriteria">Paragraph sort criteria</param>
public void Sort(SubtitleSortCriteria sortCriteria)
{
switch (sortCriteria)
{
case SubtitleSortCriteria.Number:
_paragraphs = _paragraphs.OrderBy(p => p.Number).ThenBy(p => p.StartTime.TotalMilliseconds).ToList();
2016-02-08 21:11:03 +01:00
break;
case SubtitleSortCriteria.StartTime:
_paragraphs = _paragraphs.OrderBy(p => p.StartTime.TotalMilliseconds).ThenBy(p => p.Number).ToList();
2016-02-08 21:11:03 +01:00
break;
case SubtitleSortCriteria.EndTime:
_paragraphs = _paragraphs.OrderBy(p => p.EndTime.TotalMilliseconds).ThenBy(p => p.Number).ToList();
2016-02-08 21:11:03 +01:00
break;
case SubtitleSortCriteria.Duration:
_paragraphs = _paragraphs.OrderBy(p => p.Duration.TotalMilliseconds).ThenBy(p => p.Number).ToList();
2016-02-08 21:11:03 +01:00
break;
case SubtitleSortCriteria.Text:
_paragraphs = _paragraphs.OrderBy(p => p.Text, StringComparer.Ordinal).ThenBy(p => p.Number).ToList();
2016-02-08 21:11:03 +01:00
break;
case SubtitleSortCriteria.TextMaxLineLength:
_paragraphs = _paragraphs.OrderBy(p => Utilities.GetMaxLineLength(p.Text)).ThenBy(p => p.Number).ToList();
2016-02-08 21:11:03 +01:00
break;
case SubtitleSortCriteria.TextTotalLength:
_paragraphs = _paragraphs.OrderBy(p => p.Text.Length).ThenBy(p => p.Number).ToList();
2016-02-08 21:11:03 +01:00
break;
case SubtitleSortCriteria.TextNumberOfLines:
_paragraphs = _paragraphs.OrderBy(p => p.NumberOfLines).ThenBy(p => p.Number).ToList();
2016-02-08 21:11:03 +01:00
break;
case SubtitleSortCriteria.TextCharactersPerSeconds:
_paragraphs = _paragraphs.OrderBy(Utilities.GetCharactersPerSecond).ThenBy(p => p.Number).ToList();
2016-02-08 21:11:03 +01:00
break;
case SubtitleSortCriteria.WordsPerMinute:
_paragraphs = _paragraphs.OrderBy(p => p.WordsPerMinute).ThenBy(p => p.Number).ToList();
2016-02-08 21:11:03 +01:00
break;
case SubtitleSortCriteria.Style:
_paragraphs = _paragraphs.OrderBy(p => p.Extra, StringComparer.Ordinal).ThenBy(p => p.Number).ToList();
2016-02-08 21:11:03 +01:00
break;
case SubtitleSortCriteria.Actor:
_paragraphs = _paragraphs.OrderBy(p => p.Actor, StringComparer.Ordinal).ThenBy(p => p.Number).ToList();
break;
2016-02-08 21:11:03 +01:00
}
}
2019-01-12 23:20:36 +01:00
public int InsertParagraphInCorrectTimeOrder(Paragraph newParagraph)
2016-02-08 21:11:03 +01:00
{
for (int i = 0; i < Paragraphs.Count; i++)
{
Paragraph p = Paragraphs[i];
if (newParagraph.StartTime.TotalMilliseconds < p.StartTime.TotalMilliseconds)
{
Paragraphs.Insert(i, newParagraph);
2019-01-12 23:20:36 +01:00
return i;
2016-02-08 21:11:03 +01:00
}
}
Paragraphs.Add(newParagraph);
2019-01-12 23:20:36 +01:00
return Paragraphs.Count - 1;
2016-02-08 21:11:03 +01:00
}
2019-09-29 14:33:26 +02:00
public Paragraph GetFirstParagraphOrDefaultByTime(double milliseconds)
{
foreach (var p in Paragraphs)
{
if (p.StartTime.TotalMilliseconds < milliseconds && milliseconds < p.EndTime.TotalMilliseconds)
{
return p;
}
}
return null;
}
2016-02-08 21:11:03 +01:00
/// <summary>
2019-02-10 12:23:33 +01:00
/// Fast hash code for subtitle - includes pre (encoding atm) + header + number + start + end + text.
2016-02-08 21:11:03 +01:00
/// </summary>
/// <returns>Hash value that can be used for quick compare</returns>
2019-02-10 12:23:33 +01:00
public int GetFastHashCode(string pre)
2016-02-08 21:11:03 +01:00
{
2019-02-10 12:23:33 +01:00
unchecked // Overflow is fine, just wrap
2019-01-19 14:40:37 +01:00
{
2019-02-10 12:23:33 +01:00
int hash = 17;
if (pre != null)
{
hash = hash * 23 + pre.GetHashCode();
2019-02-10 12:27:19 +01:00
}
2019-02-10 12:23:33 +01:00
if (Header != null)
{
hash = hash * 23 + Header.Trim().GetHashCode();
}
var max = Paragraphs.Count;
for (int i = 0; i < max; i++)
{
var p = Paragraphs[i];
hash = hash * 23 + p.Number.GetHashCode();
hash = hash * 23 + p.StartTime.TotalMilliseconds.GetHashCode();
hash = hash * 23 + p.EndTime.TotalMilliseconds.GetHashCode();
hash = hash * 23 + p.Text.GetHashCode();
}
return hash;
2016-02-08 21:11:03 +01:00
}
}
/// <summary>
2019-09-29 09:15:35 +02:00
/// Concatenates all Paragraphs Text property, using the default line terminator between each Text.
/// </summary>
/// <returns>Concatenated Text property of all Paragraph present in Paragraphs property.</returns>
public string GetAllTexts()
{
int max = Paragraphs.Count;
var sb = new StringBuilder(max * 40);
for (var index = 0; index < max; index++)
{
sb.AppendLine(Paragraphs[index].Text);
}
return sb.ToString();
}
2016-02-08 21:11:03 +01:00
}
}