/// <summary> /// Writes subtitle captions to file /// </summary> /// <param name="fileName">Output file name</param> /// <param name="subtitle">Text captions</param> /// <returns></returns> public static bool WriteFile(string fileName, TextSubtitle subtitle) { if (subtitle.Captions.Count == 0) { Log.Error("Subtitle contains no captions"); return(false); } var capCounter = 1; var capLines = new List <string>(); foreach (var caption in subtitle.Captions) { var startTime = DateTime.MinValue.Add(caption.StartTime).ToString("HH: mm:ss,fff", CInfo); var endTime = DateTime.MinValue.Add(caption.EndTime).ToString("HH: mm:ss, fff", CInfo); var capLine = $"{capCounter:0}{Environment.NewLine}{startTime} --> "; capLine += $"{endTime}{Environment.NewLine}{caption.Text}"; capLines.Add(capLine); capCounter++; } using (var writer = new StreamWriter(fileName)) { var separator = $"{Environment.NewLine}{Environment.NewLine}"; writer.WriteLine(string.Join(separator, capLines)); } return(true); }
/// <summary> /// Reads SRT formatted text file into single captions /// </summary> /// <param name="fileName">input file name</param> /// <returns></returns> public static TextSubtitle ReadFile(string fileName) { var result = new TextSubtitle(); if (!File.Exists(fileName)) { Log.Debug($"File \"{fileName}\" doesn't exist. Aborting file import"); return(result); } string lines; using (var reader = File.OpenText(fileName)) { lines = reader.ReadToEnd(); } if (string.IsNullOrEmpty(lines)) { return(result); } var textCaps = lines.Split(new[] { "\r\n\r\n", "\n\n" }, StringSplitOptions.RemoveEmptyEntries).ToList(); foreach (var textCap in textCaps) { var capLines = textCap.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); if (capLines.Length < 3) { continue; } var timings = capLines[1].Split(new[] { " --> " }, StringSplitOptions.RemoveEmptyEntries); if (timings.Length < 2) { continue; } var caption = new SubCaption { StartTime = DateTime.ParseExact(timings[0], "hh:mm:ss,fff", CInfo).TimeOfDay, EndTime = DateTime.ParseExact(timings[1], "hh:mm:ss,fff", CInfo).TimeOfDay, Text = string.Join(Environment.NewLine, capLines, 2, capLines.Length - 2), }; result.Captions.Add(caption); } result.SetDefaultStyle(); return(result); }
/// <summary> /// Main processing function, called by BackgroundWorker thread /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public void DoProcess(object sender, DoWorkEventArgs e) { _bw = (BackgroundWorker)sender; _bw.ReportProgress(-10, _status); _bw.ReportProgress(0, _status); SubtitleInfo sub = _jobInfo.SubtitleStreams[_jobInfo.StreamId]; string inFile = sub.TempFile; string outFile = Path.ChangeExtension(inFile, "converted.srt"); _bw.ReportProgress(0, _readingstatus); TextSubtitle textSub = null; switch (sub.Format) { case "SSA": case "ASS": textSub = SSAReader.ReadFile(inFile); break; case "UTF-8": textSub = SRTReader.ReadFile(inFile); break; } if (textSub == null) { return; } _bw.ReportProgress(50, _writingstatus); if (SRTWriter.WriteFile(outFile, textSub)) { sub.Format = "UTF-8"; sub.NeedConversion = false; _jobInfo.TempFiles.Add(inFile); sub.TempFile = outFile; _jobInfo.ExitCode = 0; } _bw.ReportProgress(100); _jobInfo.CompletedStep = _jobInfo.NextStep; e.Result = _jobInfo; }
private string GenerateCommandLine() { var sb = new StringBuilder(); var localExecutable = Path.Combine(_appConfig.ToolsPath, Executable); sb.Append($"-jar \"{localExecutable}\" "); _subtitle = _currentTask.SubtitleStreams[_currentTask.StreamId]; var targetRes = -1; if (_currentTask.EncodingProfile.OutFormat == OutputType.OutputDvd) { targetRes = _currentTask.EncodingProfile.SystemType == 0 ? 576 : 480; } _inputFile = _subtitle.TempFile; TextSubtitle textSub = null; switch (_subtitle.Format) { case "SSA": case "ASS": textSub = SsaReader.ReadFile(_inputFile); break; case "UTF-8": textSub = SrtReader.ReadFile(_inputFile); break; } var inFileDir = Path.GetDirectoryName(_inputFile); if (string.IsNullOrEmpty(inFileDir)) { inFileDir = string.Empty; } var inFileName = Path.GetFileNameWithoutExtension(_inputFile); if (string.IsNullOrEmpty(inFileName)) { inFileName = string.Empty; } var outPath = Path.Combine(inFileDir, inFileName); if (Directory.Exists(outPath)) { Directory.Delete(outPath, true); } Directory.CreateDirectory(outPath, DirSecurity.CreateDirSecurity(SecurityClass.Everybody)); var inFileFullName = Path.GetFileName(_inputFile); if (string.IsNullOrEmpty(inFileFullName)) { inFileFullName = string.Empty; } _outputFile = Path.Combine(outPath, inFileFullName); if (textSub != null) { var xmlFile = Path.ChangeExtension(_outputFile, "xml"); if (BdnExport.WriteBdnXmlFile(textSub, xmlFile, _currentTask.VideoStream.Width, _currentTask.VideoStream.Height, _currentTask.VideoStream.Fps)) { _subtitle.Format = "XML"; _currentTask.TempFiles.Add(_inputFile); _subtitle.TempFile = xmlFile; _inputFile = xmlFile; } } if (_currentTask.EncodingProfile.OutFormat == OutputType.OutputDvd) { _outputFile = Path.ChangeExtension(_outputFile, "processed.xml"); } else if (_subtitle.KeepOnlyForcedCaptions) { _outputFile = Path.ChangeExtension(_outputFile, "forced.sup"); } else if (_subtitle.Format == "XML" || _subtitle.Format == "VobSub") { _outputFile = Path.ChangeExtension(_outputFile, "sup"); } var targetFps = _currentTask.VideoStream.FrameMode.Trim().ToLowerInvariant() == "frame doubling" ? _currentTask.VideoStream.Fps * 2 : _currentTask.VideoStream.Fps; var fpsMode = "keep"; if (Math.Abs(targetFps - _currentTask.VideoStream.Fps) > 0) { fpsMode = targetFps.ToString("0.000", _appConfig.CInfo); } sb.Append($"\"{_inputFile}\" --output \"{_outputFile}\" --fps-target {fpsMode} --palette-mode keep "); if (_subtitle.KeepOnlyForcedCaptions) { sb.Append("--forced-only "); } if (_currentTask.EncodingProfile.OutFormat == OutputType.OutputDvd) { sb.Append($" --resolution {targetRes:0} "); } return(sb.ToString()); }
/// <summary> /// Main processing function, called by BackgroundWorker thread /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public void DoProcess(object sender, DoWorkEventArgs e) { _bw = (BackgroundWorker)sender; string createSubtitle = Processing.GetResourceString("bdsup2sub_convert_subtitle_create"); _bw.ReportProgress(-10, _status); _bw.ReportProgress(0, _status); string javaExecutable = AppSettings.JavaInstallPath; string localExecutable = Path.Combine(AppSettings.ToolsPath, Executable); SubtitleInfo sub = _jobInfo.SubtitleStreams[_jobInfo.StreamId]; StringBuilder sb = new StringBuilder(); sb.AppendFormat("-jar \"{0}\" ", localExecutable); int targetRes = -1; if (_jobInfo.EncodingProfile.OutFormat == OutputType.OutputDvd) { targetRes = _jobInfo.EncodingProfile.SystemType == 0 ? 576 : 480; } string inFile = sub.TempFile; TextSubtitle textSub = null; switch (sub.Format) { case "SSA": case "ASS": textSub = SSAReader.ReadFile(inFile); break; case "UTF-8": textSub = SRTReader.ReadFile(inFile); break; } string inFileDir = Path.GetDirectoryName(inFile); if (string.IsNullOrEmpty(inFileDir)) { inFileDir = string.Empty; } string inFileName = Path.GetFileNameWithoutExtension(inFile); if (string.IsNullOrEmpty(inFileName)) { inFileName = string.Empty; } string outPath = Path.Combine(inFileDir, inFileName); if (Directory.Exists(outPath)) { Directory.Delete(outPath, true); } Directory.CreateDirectory(outPath, DirSecurity.CreateDirSecurity(SecurityClass.Everybody)); string inFileFullName = Path.GetFileName(inFile); if (string.IsNullOrEmpty(inFileFullName)) { inFileFullName = string.Empty; } string outFile = Path.Combine(outPath, inFileFullName); if (textSub != null) { string xmlFile = Path.ChangeExtension(outFile, "xml"); if (BDNExport.WriteBDNXmlFile(textSub, xmlFile, _jobInfo.VideoStream.Width, _jobInfo.VideoStream.Height, _jobInfo.VideoStream.FPS)) { sub.Format = "XML"; _jobInfo.TempFiles.Add(inFile); sub.TempFile = xmlFile; inFile = xmlFile; } } if (_jobInfo.EncodingProfile.OutFormat == OutputType.OutputDvd) { outFile = Path.ChangeExtension(outFile, "processed.xml"); } else if (sub.KeepOnlyForcedCaptions) { outFile = Path.ChangeExtension(outFile, "forced.sup"); } else if (sub.Format == "XML" || sub.Format == "VobSub") { outFile = Path.ChangeExtension(outFile, "sup"); } float targetFPS = _jobInfo.VideoStream.FrameMode.Trim().ToLowerInvariant() == "frame doubling" ? _jobInfo.VideoStream.FPS * 2 : _jobInfo.VideoStream.FPS; string fpsMode = "keep"; if (Math.Abs(targetFPS - _jobInfo.VideoStream.FPS) > 0) { fpsMode = targetFPS.ToString("0.000", AppSettings.CInfo); } sb.AppendFormat(AppSettings.CInfo, "\"{0:s}\" --output \"{1:s}\" --fps-target {2} --palette-mode keep ", inFile, outFile, fpsMode); if (sub.KeepOnlyForcedCaptions) { sb.AppendFormat("--forced-only "); } if (_jobInfo.EncodingProfile.OutFormat == OutputType.OutputDvd) { sb.AppendFormat(AppSettings.CInfo, " --resolution {0:0} ", targetRes); } using (Process encoder = new Process()) { ProcessStartInfo parameter = new ProcessStartInfo(javaExecutable) { WorkingDirectory = AppSettings.DemuxLocation, CreateNoWindow = true, UseShellExecute = false, RedirectStandardOutput = true, Arguments = sb.ToString() }; encoder.StartInfo = parameter; encoder.OutputDataReceived += EncoderOnOutputDataReceived; Log.InfoFormat("BDSup2Sub: {0:s}", parameter.Arguments); bool started; try { started = encoder.Start(); } catch (Exception ex) { started = false; Log.ErrorFormat("BDSup2Sub exception: {0}", ex); _jobInfo.ExitCode = -1; } if (started) { encoder.PriorityClass = AppSettings.GetProcessPriority(); encoder.BeginOutputReadLine(); while (!encoder.HasExited) { if (_bw.CancellationPending) { encoder.Kill(); } Thread.Sleep(200); } _jobInfo.ExitCode = encoder.ExitCode; Log.InfoFormat("Exit Code: {0:g}", _jobInfo.ExitCode); } if (_jobInfo.ExitCode == 0) { _jobInfo.TempFiles.Add(inFile); if (sub.Format == "XML") { GetTempImages(inFile); } if (sub.Format == "VobSub") { _jobInfo.TempFiles.Add(Path.ChangeExtension(inFile, "sub")); } if (_jobInfo.EncodingProfile.OutFormat == OutputType.OutputDvd) { _jobInfo.TempFiles.Add(outFile); _bw.ReportProgress(-1, createSubtitle); sub.TempFile = GenerateSpuMuxSubtitle(outFile); sub.Format = "SpuMux"; } else { sub.TempFile = outFile; sub.Format = "PGS"; } sub.NeedConversion = false; } } _bw.ReportProgress(100); _jobInfo.CompletedStep = _jobInfo.NextStep; e.Result = _jobInfo; }
// TODO: look into xml generation /// <summary> /// Writes BDN File /// </summary> /// <param name="subtitle">Text subtitle to write</param> /// <param name="fileName">Output file name</param> /// <param name="videoWidth">Target video width</param> /// <param name="videoHeight">Target video height</param> /// <param name="fps">Target video fps</param> /// <returns></returns> public static bool WriteBdnXmlFile(TextSubtitle subtitle, string fileName, int videoWidth, int videoHeight, float fps) { if (File.Exists(fileName)) { return(false); } var partFileName = Path.GetFileNameWithoutExtension(fileName); var outputDocument = new XmlDocument(); outputDocument.AppendChild(outputDocument.CreateXmlDeclaration("1.0", "UTF-8", null)); XmlNode docNode = outputDocument.CreateElement("BDN"); outputDocument.AppendChild(docNode); AppendAttribute(docNode, "Version", "0.93", outputDocument); AppendAttribute(docNode, "xsi", "noNamespaceSchemaLocation", "http://www.w3.org/2001/XMLSchema-instance", "BD-03-006-0093b BDN File Format.xsd", outputDocument); XmlNode descNode = outputDocument.CreateElement("Description"); docNode.AppendChild(descNode); XmlNode workNode = outputDocument.CreateElement("Name"); AppendAttribute(workNode, "Title", partFileName, outputDocument); AppendAttribute(workNode, "Content", string.Empty, outputDocument); descNode.AppendChild(workNode); workNode = outputDocument.CreateElement("Language"); AppendAttribute(workNode, "Code", string.Empty, outputDocument); descNode.AppendChild(workNode); workNode = outputDocument.CreateElement("Format"); AppendAttribute(workNode, "VideoFormat", $"{videoHeight:0}p", outputDocument); AppendAttribute(workNode, "FrameRate", $"{fps:0.000}".ToString(CInfo), outputDocument); AppendAttribute(workNode, "DropFrame", "False", outputDocument); descNode.AppendChild(workNode); workNode = outputDocument.CreateElement("Events"); AppendAttribute(workNode, "Type", "Graphic", outputDocument); AppendAttribute(workNode, "FirstEventInTC", CreateBdnTimeStamp(subtitle.Captions.First().StartTime, fps), outputDocument); AppendAttribute(workNode, "LastEventOutTC", CreateBdnTimeStamp(subtitle.Captions.Last().EndTime, fps), outputDocument); AppendAttribute(workNode, "NumberofEvents", subtitle.Captions.Count.ToString(CInfo), outputDocument); descNode.AppendChild(workNode); XmlNode eventNode = outputDocument.CreateElement("Events"); docNode.AppendChild(eventNode); var i = 0; foreach (var caption in subtitle.Captions) { var image = PngImage.CreateImage(caption, subtitle.Style, i, videoWidth, videoHeight, fileName); if (string.IsNullOrEmpty(image.FileName) || image.Width == 0 || image.Height == 0) { continue; } workNode = outputDocument.CreateElement("Event"); AppendAttribute(workNode, "InTC", CreateBdnTimeStamp(caption.StartTime, fps), outputDocument); AppendAttribute(workNode, "OutTC", CreateBdnTimeStamp(caption.EndTime, fps), outputDocument); AppendAttribute(workNode, "Forced", "False", outputDocument); eventNode.AppendChild(workNode); XmlNode gNode = outputDocument.CreateElement("Graphic"); AppendAttribute(gNode, "Width", image.Width.ToString(CInfo), outputDocument); AppendAttribute(gNode, "Height", image.Height.ToString(CInfo), outputDocument); var posX = (int)Math.Ceiling((float)videoWidth / 2 - (float)image.Width / 2); var posY = videoHeight - image.Height - 120; AppendAttribute(gNode, "X", posX.ToString(CInfo), outputDocument); AppendAttribute(gNode, "Y", posY.ToString(CInfo), outputDocument); gNode.InnerText = image.FileName; workNode.AppendChild(gNode); i++; } outputDocument.Save(fileName); return(true); }
/// <summary> /// Read subtitle file /// </summary> /// <param name="fileName">Textfile</param> /// <returns>parsed <see cref="TextSubtitle"/></returns> public static TextSubtitle ReadFile(string fileName) { var result = new TextSubtitle(); if (!File.Exists(fileName)) { Log.Debug($"File \"{fileName}\" doesn't exist. Aborting file import"); return(result); } string lines; using (TextReader reader = File.OpenText(fileName)) { lines = reader.ReadToEnd(); } if (string.IsNullOrEmpty(lines)) { return(result); } var textLines = lines.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries).ToList(); var textCaps = new List <string>(); var sb = new StringBuilder(); foreach (var line in textLines) { if (line.Trim().StartsWith("[")) { if (sb.Length > 0) { textCaps.Add(sb.ToString()); sb.Clear(); } sb.AppendLine(line.Trim()); } else { sb.AppendLine(line.Trim()); } } if (sb.Length > 0) { textCaps.Add(sb.ToString()); } sb.Clear(); //textCaps = lines.Split(new[] {"\r\n\r\n", "\n\n"}, StringSplitOptions.RemoveEmptyEntries).ToList(); if (!textCaps.Any()) { return(result); } var sInfo = textCaps[0]; if (!sInfo.Any()) { return(result); } sInfo = Regex.Replace(sInfo, "^;.*$|^Title.*$", "", RegexOptions.Multiline); var isAdvancedScript = false; try { var matchAdvanced = Regex.Match(sInfo, @"^\[Script Info\].*ScriptType: v4\.00+.*$", RegexOptions.Singleline | RegexOptions.Multiline); var matchResults = Regex.Match(sInfo, @"^\[Script Info\].*ScriptType: v4\.00.*$", RegexOptions.Singleline | RegexOptions.Multiline); if (!matchResults.Success && !matchAdvanced.Success) { return(result); } if (matchAdvanced.Success) { isAdvancedScript = true; } } catch (ArgumentException ex) { Log.Error(ex); } var setFormat = false; for (var i = 1; i <= textCaps.Count - 1; i++) { var section = textCaps[i]; try { var matchStyles = Regex.Match(section, @"^\[V4.*Styles\].*$", RegexOptions.Multiline); var matchEvents = Regex.Match(section, @"^\[Events\].*$", RegexOptions.Multiline); if (matchStyles.Success && !setFormat) { var styles = section.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); if (styles.Length < 3) { return(result); } var formatAttribs = styles[1].Split(new[] { "," }, StringSplitOptions.None).ToList(); var formatValues = styles[2].Split(new[] { "," }, StringSplitOptions.None).ToList(); for (var index = 0; index <= formatAttribs.Count - 1; index++) { var formatAttrib = formatAttribs[index].Trim(); var formatValue = formatValues[index].Trim(); switch (formatAttrib) { case "Fontname": result.Style.FontName = formatValue; break; case "Fontsize": result.Style.FontSize = int.Parse(formatValue, NumberStyles.Integer); break; case "PrimaryColour": result.Style.PrimaryColor = GetCorrectColor(formatValue); break; case "SecondaryColour": result.Style.SecondaryColor = GetCorrectColor(formatValue); break; case "TertiaryColour": case "OutlineColour": result.Style.OutlineColor = GetCorrectColor(formatValue); break; case "BackColour": result.Style.BackColor = GetCorrectColor(formatValue); break; case "Bold": result.Style.Bold = formatValue == "-1"; break; case "Italic": result.Style.Italic = formatValue == "-1"; break; case "Underline": result.Style.Underline = formatValue == "-1"; break; case "StrikeOut": result.Style.StrikeThrough = formatValue == "-1"; break; case "BorderStyle": result.Style.BorderStyle = int.Parse(formatValue, NumberStyles.Integer); break; case "Outline": result.Style.Outline = int.Parse(formatValue, NumberStyles.Integer); break; case "Shadow": result.Style.Shadow = int.Parse(formatValue, NumberStyles.Integer); break; case "Alignment": result.Style.Alignment = GetAlignment(formatValue, isAdvancedScript); break; case "MarginL": result.Style.MarginL = int.Parse(formatValue, NumberStyles.Integer); break; case "MarginR": result.Style.MarginR = int.Parse(formatValue, NumberStyles.Integer); break; case "MarginV": result.Style.MarginV = int.Parse(formatValue, NumberStyles.Integer); break; case "Alphalevel": result.Style.AlphaLevel = int.Parse(formatValue, NumberStyles.Integer); break; case "Encoding": result.Style.Encoding = formatValue; break; } } setFormat = true; } else if (matchEvents.Success) { var events = section.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); if (events.Length < 2) { return(result); } var eventFormat = events[1].Split(new[] { "," }, StringSplitOptions.None).ToList(); int startTimeRow = -1, endTimeRow = -1, textRow = -1; for (var j = 0; j <= eventFormat.Count - 1; j++) { var label = eventFormat[j].Trim(); if (label.Equals("Start")) { startTimeRow = j; } else if (label.Equals("End")) { endTimeRow = j; } else if (label.Equals("Text")) { textRow = j; } } if (startTimeRow < 0 || endTimeRow < 0 || textRow < 0) { return(result); } for (var j = 2; j <= events.Length - 1; j++) { var eventLine = events[j]; var eventRows = eventLine.Split(new[] { "," }, StringSplitOptions.None); if (eventRows.Length < textRow + 1) { continue; } var startTime = DateTime.ParseExact(eventRows[startTimeRow].Trim(), "h:mm:ss.ff", CInfo). TimeOfDay; var endTime = DateTime.ParseExact(eventRows[endTimeRow].Trim(), "h:mm:ss.ff", CInfo). TimeOfDay; var text = string.Join(", ", eventRows, textRow, eventRows.Length - textRow); // line break text = Regex.Replace(text, @"(?:\\N|\\n)", Environment.NewLine, RegexOptions.Multiline); // remove line break strategy text = Regex.Replace(text, @"\\q\d*?", string.Empty, RegexOptions.Multiline); // bold text text = Regex.Replace(text, @"\{\\b1\}(.*?)\{\\b0*?\}", "<b>$1</b>", RegexOptions.Singleline | RegexOptions.Multiline); // italic text text = Regex.Replace(text, @"\{\\i1\}(.*?)\{\\i0*?\}", "<i>$1</i>", RegexOptions.Singleline | RegexOptions.Multiline); // underlined text text = Regex.Replace(text, @"\{\\u1\}(.*?)\{\\u0*?\}", "<u>$1</u>", RegexOptions.Singleline | RegexOptions.Multiline); // strike-through text text = Regex.Replace(text, @"\{\\s1\}(.*?)\{\\s0*?\}", "<s>$1</s>", RegexOptions.Singleline | RegexOptions.Multiline); // remove border and shadow override text = Regex.Replace(text, @"\{\\(?:bord|shad)\d*?\}", string.Empty, RegexOptions.Multiline); // remove blurry text border text = Regex.Replace(text, @"\{\\be(?:1|0)\}", string.Empty, RegexOptions.Multiline); // remove fontname text = Regex.Replace(text, @"\{\\fn.*\}", string.Empty, RegexOptions.Multiline); // remove fontsize text = Regex.Replace(text, @"\{\\fs.*\}", string.Empty, RegexOptions.Multiline); // remove color definition text = Regex.Replace(text, @"\{\\\d?c&H.*&\}", string.Empty, RegexOptions.Multiline); // remove alpha definition text = Regex.Replace(text, @"\{\\\d?(?:a|alpha)&H.*&\}", string.Empty, RegexOptions.Multiline); // remove x/y text scaling text = Regex.Replace(text, @"\{\\(?:fscy|fscx)\d+\}", string.Empty, RegexOptions.Multiline); // remove text spacing text = Regex.Replace(text, @"\{\\fsp\d+\}", string.Empty, RegexOptions.Multiline); // remove charset definition text = Regex.Replace(text, @"\{\\fe.*?\}", string.Empty, RegexOptions.Multiline); // parse and remove text alignment var align = new Regex(@"\{\\an*?(\d*?)\}", RegexOptions.Multiline); var alignment = GetAlignment(align.Match(text).Value, isAdvancedScript); text = align.Replace(text, string.Empty); // remove x/y/z text rotation text = Regex.Replace(text, @"\{\\fr(?:x|y|z)??(-??\d*?)\}", string.Empty, RegexOptions.Multiline); // remove karaoke formatting text = Regex.Replace(text, @"\{\\(?:k|ko|kf|K)(\d*?)\}", string.Empty, RegexOptions.Multiline); // remove format reset text = Regex.Replace(text, @"\{\\r.*\}", string.Empty, RegexOptions.Multiline); // remove text animation text = Regex.Replace(text, @"\{\\(?:move|pos|t|org|fad|fade|clip)\(.*?\)\}", string.Empty, RegexOptions.Multiline); // remove anything that was not catched above text = Regex.Replace(text, @"\{(?:\\(?:fscy|fscx)\d+|\\fn.*|\\fs.*|\\\d?c&H.*&|\\\d?(?:a|alpha)&H.*&|\\(?:fscy|fscx)\d+|" + @"\\fsp\d+|\\fe.*?|\\an*?(\d*?)|\\fr(?:x|y|z)??(-??\d*?)|\\(?:k|ko|kf|K)(\d*?)|\\r.*|" + @"\\(?:move|pos|t|org|fad|fade|clip)\(.*?\)\\N|\\n|\\q\d*?|\\(?:b|i|u|s|be)(?:1|0)*?|\\(?:bord|shad)\d*?)*?\}", string.Empty, RegexOptions.Multiline); var caption = new SubCaption { StartTime = startTime, EndTime = endTime, Text = text, Alignment = alignment }; result.Captions.Add(caption); } } } catch (ArgumentException ex) { Log.Error(ex); } } if (!setFormat) { result.SetDefaultStyle(); } return(result); }