예제 #1
0
        /// <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);
        }
예제 #2
0
        /// <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;
        }
예제 #4
0
        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());
        }
예제 #5
0
        /// <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;
        }
예제 #6
0
        // 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);
        }
예제 #7
0
        /// <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);
        }