// Public Methods (1) public static void DeleteWholeRow(SubtitleItems data, SubtitleItem toDelete, string fileNameToSave) { if (data == null || toDelete == null || string.IsNullOrWhiteSpace(fileNameToSave)) { LogWindow.AddMessage(LogType.Alert, "Please select a line to delete it."); return; } var number = toDelete.Number; if (data.Remove(toDelete)) { //fix numbers for (var i = 0; i < data.Count; i++) { data[i].Number = i + 1; } //backup original file var backupFile = string.Format("{0}.original.bak", fileNameToSave); File.Copy(fileNameToSave, backupFile, overwrite: true); //write data File.WriteAllText(fileNameToSave, ParseSrt.SubitemsToString(data).ApplyCorrectYeKe(), Encoding.UTF8); LogWindow.AddMessage(LogType.Info, string.Format("Line {0} has been deleted.", number)); LogWindow.AddMessage(LogType.Info, string.Format("Backup file: {0}", backupFile)); } else { LogWindow.AddMessage(LogType.Alert, string.Format("Couldn't delete line {0}", number)); } }
//public List<SubtitleItem> items = new List<SubtitleItem>(); //public SortedSet<string> words = new SortedSet<string>(); protected bool AddWords(SubtitleItem item) { if (item == null || item.Lines.Count == 0) { return(false); } bool add = false; foreach (string line in item.Lines) { string[] words; if (TryParseWords(line, out words) && words.Length > 0) { add = true; foreach (string word in words) { item.Words.Add(word); } } } return(add); }
// Private Methods (43) private void addSubtitleToFile(SubtitleItem subtitleItem, string mediaPath) { if (string.IsNullOrWhiteSpace(subtitleItem.Dialog)) { return; } var subtitleFilePath = string.Empty; if (string.IsNullOrWhiteSpace(MainWindowGuiData.OpenedFilePath)) { if (!string.IsNullOrWhiteSpace(mediaPath)) { subtitleFilePath = createEmptySubtitleFile(mediaPath); } else { LogWindow.AddMessage(LogType.Error, "Please open an empty subtitle file first."); return; } } subtitleItemsDataInternal = ParseSrt.AddSubtitleItemToList(subtitleItemsDataInternal, subtitleItem); setFlowDir(subtitleItem.Dialog.ContainsFarsi()); saveToFile(subtitleFilePath); _lastStartTs = subtitleItem.StartTs; doScrollToIndex(subtitleItemsDataInternal.Count - 1); if (!string.IsNullOrEmpty(subtitleFilePath)) { MainWindowGuiData.OpenedFilePath = subtitleFilePath; } App.Messenger.NotifyColleagues("doClearSubtitle"); }
/// <summary> /// Parses one line of the .sub file /// /// ex: /// {0}{180}PIRATES OF THE CARIBBEAN|English subtitlez by tHe.b0dY /// </summary> /// <param name="line">The .sub file line</param> /// <param name="frameRate">The frame rate with which the .sub file was created</param> /// <returns>The corresponding SubtitleItem</returns> private SubtitleItem ParseLine(string line, float frameRate) { var match = Regex.Match(line, LineRegex); if (match.Success && match.Groups.Count > 2) { var startFrame = match.Groups[1].Value; var start = (int)(1000 * double.Parse(startFrame) / frameRate); var endTime = match.Groups[2].Value; var end = (int)(1000 * double.Parse(endTime) / frameRate); var text = match.Groups[match.Groups.Count - 1].Value; var lines = text.Split(_lineSeparators); var nonEmptyLines = lines.Where(l => !string.IsNullOrEmpty(l)).ToList(); var item = new SubtitleItem { Lines = nonEmptyLines, StartTime = start, EndTime = end }; return(item); } else { var message = string.Format("The subtitle file line {0} is " + "not in the micro dvd format. We stop the process.", line); throw new InvalidDataException(message); } }
public string GetTimeStringFor(SubtitleItem item) { var startTimeStr = (item.StartTime / 1000.0).ToString("#.#s"); var endTimeStr = (item.EndTime / 1000.0).ToString("#.#s"); return($"{startTimeStr} → {endTimeStr}"); }
// Public Methods (11) public static SubtitleItems AddSubtitleItemToList(SubtitleItems subtitleItemsDataInternal, SubtitleItem subtitleItem) { var localSubItems = subtitleItemsDataInternal.ToList(); var newItem = new SubtitleItem { Dialog = subtitleItem.Dialog, EndTs = subtitleItem.EndTs, StartTs = subtitleItem.StartTs }; localSubItems.Add(newItem); localSubItems = localSubItems.OrderBy(x => x.StartTs).ToList(); var i = 1; var finalItems = new SubtitleItems(); foreach (var item in localSubItems) { item.Number = i++; finalItems.Add(item); } return(finalItems); }
public static string Header(this SubtitleItem s) { var startTime = new TimeSpan(0, 0, 0, 0, s.StartTime); var endTime = new TimeSpan(0, 0, 0, 0, s.EndTime); return ($"{startTime.ToString(TimeFormat, CultureInfo.InvariantCulture)} --> {endTime.ToString(TimeFormat, CultureInfo.InvariantCulture)}"); }
private string ConvertItem(SubtitleItem item) { var result = string.IsNullOrEmpty(item.Text) ? " " : Regex.Replace(item.Text, "(\r\n|\r|\n)", "<br>"); return($"<SYNC Start={item.StartTime}><P>{result}"); }
/// <summary> /// /// </summary> /// <param name="item"></param> /// <returns></returns> public static MySubtitleItem CopyToMySubtitleItem(this SubtitleItem item) { if (item == null) { throw new ArgumentNullException(nameof(item)); } return(new MySubtitleItem(s: item.StartTime, e: item.EndTime, l: item.Lines.ToList())); }
public List <SubtitleItem> ParseStream(Stream xmlStream, Encoding encoding) { // rewind the stream xmlStream.Position = 0; var items = new List <SubtitleItem>(); // parse xml stream var xmlDoc = new XmlDocument(); xmlDoc.Load(xmlStream); if (xmlDoc.DocumentElement != null) { var nodeList = xmlDoc.DocumentElement.SelectNodes("//text"); if (nodeList != null) { for (var i = 0; i < nodeList.Count; i++) { var node = nodeList[i]; try { var startString = node.Attributes["start"].Value; float start = float.Parse(startString, CultureInfo.InvariantCulture); var durString = node.Attributes["dur"].Value; float duration = float.Parse(durString, CultureInfo.InvariantCulture); var text = node.InnerText; SubtitleItem item = new SubtitleItem() { StartTime = (int)(start * 1000), EndTime = (int)((start + duration) * 1000), Lines = new List <string>() { text } }; items.Add(item); } catch (Exception ex) { Console.WriteLine("Exception raised when parsing xml node {0}: {1}", node, ex); } } } } if (items.Any()) { return(items); } else { throw new ArgumentException("Stream is not in a valid Youtube XML format"); } }
public static SubtitleItem Clone(this SubtitleItem s1) { return(new SubtitleItem() { StartTime = s1.StartTime, EndTime = s1.EndTime, Lines = new System.Collections.Generic.List <string>(s1.Lines), }); }
public void WriteWav(SubtitleItem info, string episode) { string output = WavName(info, EpisodeCode(episode)); Process.Start("ffmpeg", $"-hide_banner -i \"{Path.GetFullPath(episode)}\" -ss {info.StartTime} -to {info.EndTime} -f wav {Path.Combine(WavPath, output)} "); UpdateCSV(info, output, episode); AddToSkip(info, episode); }
/// <summary> /// Đếm thời gian để thay đổi sub /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void timer1_Tick(object sender, EventArgs e) { if (subItems == null) { return; } int cp = Convert.ToInt32(WMPMain.Ctlcontrols.currentPosition * 1000); if (cp < subItems[0].StartTime || cp > subItems[subItems.Count - 1].EndTime) { return; } if (currentSubIndex + 1 < subItems.Count) { if (cp >= subItems[currentSubIndex].StartTime && cp < subItems[currentSubIndex + 1].StartTime) { return; } if (cp >= subItems[currentSubIndex + 1].StartTime && cp <= subItems[currentSubIndex + 1].EndTime) { currentSubIndex++; boldCurrentSub(); return; } } //Tìm sub nếu như tua một đoạn //Debug.WriteLine("Find sub = " + WMPMain.Ctlcontrols.currentPosition + " " + subItems[currentSubIndex].StartTime / 1000.0 + " " + currentSubIndex); SubtitleItem searchItem = new SubtitleItem(); searchItem.StartTime = cp; int index = ~subItems.BinarySearch(searchItem, new StartTimeComparer());//index = phần tử liền sau, index có thể = count index = Math.Abs(index); //Debug.WriteLine("Find sub = " + index); if (index > 0) { index--;//Do index = phần tử liền sau if (index + 1 < subItems.Count) { if (cp >= subItems[index].StartTime && cp < subItems[index + 1].StartTime) { currentSubIndex = index; boldCurrentSub(); } } else { currentSubIndex = subItems.Count - 1; boldCurrentSub(); } } }
private void Dt_Tick(object sender, object e) { try { if (SubtitleList != null && SubtitleList.Any()) { var v = (from item in SubtitleList where item != null && item.StartTime + seekDouble <= MyPlayer.MediaPlayer.PlaybackSession.Position.TotalMilliseconds && item.EndTime + seekDouble >= MyPlayer.MediaPlayer.PlaybackSession.Position.TotalMilliseconds orderby item descending select item).FirstOrDefault(); CurrentSubtitleItem = v; if (v != null) { richtextblock.Blocks.Clear(); Paragraph myParagraph = new Paragraph(); int nextParagraph = 1; string paragraph = ""; foreach (string item in v.Lines) { paragraph += item.Trim().ToString() + "\r\n"; if (GetRun(item) != null) { myParagraph.Inlines.Add(GetRun(item.Trim())); try { if (v.Lines[nextParagraph] != null) { myParagraph.Inlines.Add(new LineBreak()); } } catch (Exception ex) { Debug.WriteLine("nextParagraph ex: " + ex.Message); } } nextParagraph++; } //Run run = new Run(); //run.Text = paragraph.Trim(); //myParagraph.Inlines.Add(run); richtextblock.Blocks.Add(myParagraph); border.Visibility = Visibility.Visible; } else { border.Visibility = Visibility.Collapsed; richtextblock.Blocks.Clear(); } } else { richtextblock.Blocks.Clear(); } } catch (Exception ex) { Debug.WriteLine("mediaPlayer_PositionChanged ex: " + ex.Message); } }
public static SubtitleRow ToSubtitleRow(this SubtitleItem item) { SubtitleRow result = new SubtitleRow(); result.StartTime = new TimeSpan(0, 0, 0, 0, item.StartTime); result.EndTime = new TimeSpan(0, 0, 0, 0, item.EndTime); result.Value = String.Join(" ", item.Lines); return(result); }
public static void FixMinReadTime(this SubtitleItem subtitleItem) { var totalSeconds = (subtitleItem.EndTs - subtitleItem.StartTs).TotalSeconds; var readTime = subtitleItem.Dialog.MinReadTime(); var minReadTime = readTime.TotalSeconds; if (totalSeconds < minReadTime) { subtitleItem.EndTs = subtitleItem.StartTs.Add(readTime); } }
public static bool Overlaps(this SubtitleItem s1, SubtitleItem s2) { if (s1.StartTime <= s2.StartTime) { return(s1.EndTime > s2.StartTime); } else { return(s2.Overlaps(s1)); } }
public List <SubtitleItem> ParseStream(Stream xmlStream, Encoding encoding) { // rewind the stream xmlStream.Position = 0; var items = new List <SubtitleItem>(); // parse xml stream var xElement = XElement.Load(xmlStream); var tt = xElement.GetNamespaceOfPrefix("tt") ?? xElement.GetDefaultNamespace(); var nodeList = xElement.Descendants(tt + "p").ToList(); foreach (var node in nodeList) { try { var reader = node.CreateReader(); reader.MoveToContent(); var beginString = node.Attribute("begin").Value.Replace("t", ""); var startTicks = ParseTimecode(beginString); var endString = node.Attribute("end").Value.Replace("t", ""); var endTicks = ParseTimecode(endString); var text = reader.ReadInnerXml() .Replace("<tt:", "<") .Replace("</tt:", "</") .Replace(string.Format(@" xmlns:tt=""{0}""", tt), "") .Replace(string.Format(@" xmlns=""{0}""", tt), ""); SubtitleItem item = new SubtitleItem() { StartTime = (int)(startTicks), EndTime = (int)(endTicks), Lines = new List <string>() { text } }; AddWords(item); items.Add(item); } catch (Exception ex) { Console.WriteLine("Exception raised when parsing xml node {0}: {1}", node, ex); } } if (items.Any()) { return(items); } throw new ArgumentException("Stream is not in a valid TTML format, or represents empty subtitles"); }
public bool CanSkip(SubtitleItem info, string episode) { string epCode = EpisodeCode(episode), startTime = info.StartTime.ToString(); //If that episode and timecode has been tested return(AlreadyTested.AsParallel().Any(s => { return s[0] == epCode && s[1] == startTime; })); }
private void initLocalItems() { _subtitleItems = new SubtitleItems(); PreviewModelData = new PreviewModel(); SubtitleItemData = new SubtitleItem(); PreviewModelData.PlayImage = "play"; PreviewModelData.DragCompleted = true; PreviewModelData.PropertyChanged += previewModelDataPropertyChanged; SubtitleItemData.PropertyChanged += subtitleItemDataPropertyChanged; SubtitleItemData.CaretIndex = 1; }
private void Dt_Tick(object sender, object e) { try { if (SubtitleList != null && SubtitleList.Any() && showSubtitle) { var v = (from item in SubtitleList where item != null && item.StartTime + seekDouble <= mediaElement.Position.TotalMilliseconds && item.EndTime + seekDouble >= mediaElement.Position.TotalMilliseconds orderby item descending select item).FirstOrDefault(); CurrentSubtitleItem = v; if (v != null) { customMTC.SubtitleText.Blocks.Clear(); Paragraph myParagraph = new Paragraph(); int nextParagraph = 1; foreach (string item in v.Lines) { if (GetRun(item) != null) { myParagraph.Inlines.Add(GetRun(item.Replace("ي", "ی"))); try { if (v.Lines.Count < nextParagraph && v.Lines[nextParagraph] != null) { myParagraph.Inlines.Add(new LineBreak()); } } catch (Exception ex) { HelperUP.Output("nextParagraph ex: " + ex.Message); } } nextParagraph++; } customMTC.SubtitleText.Blocks.Add(myParagraph); customMTC.ShadowBorder.Visibility = customMTC.SubtitleText.Visibility = Windows.UI.Xaml.Visibility.Visible; } else { customMTC.ShadowBorder.Visibility = customMTC.SubtitleText.Visibility = Windows.UI.Xaml.Visibility.Collapsed; customMTC.SubtitleText.Blocks.Clear(); } } else { customMTC.SubtitleText.Blocks.Clear(); } } catch (Exception ex) { HelperUP.Output("mediaPlayer_PositionChanged ex: " + ex.Message); } }
void SeekToNextSubtitle(bool next = true) { if (PlayerPage.Current == null || PlayerPage.Current != null && PlayerPage.Current.SubtitleList == null) { return; } if (PlayerPage.Current.CurrentSubtitleItem == null) { return; } var sub = PlayerPage.Current.CurrentSubtitleItem; var index = PlayerPage.Current.SubtitleList.FindIndex(item => item.StartTime == sub.StartTime && item.EndTime == sub.EndTime); if (next) { if (index > 0) { if (index + 1 <= PlayerPage.Current.SubtitleList.Count) { try { SubtitleItem itemSub = PlayerPage.Current.SubtitleList[index + 1]; PlayerPage.Current.MediaElement.Pause(); PlayerPage.Current.MediaElement.Position = TimeSpan.FromMilliseconds(itemSub.StartTime); PlayerPage.Current.MediaElement.Play(); } catch { } } } } else { if (index > 0) { if (index - 1 <= PlayerPage.Current.SubtitleList.Count) { try { SubtitleItem itemSub = PlayerPage.Current.SubtitleList[index - 1]; PlayerPage.Current.MediaElement.Pause(); PlayerPage.Current.MediaElement.Position = TimeSpan.FromMilliseconds(itemSub.StartTime); PlayerPage.Current.MediaElement.Play(); } catch { } } } } }
public static List <SubtitleItem> RemoveDuplicateItems(List <SubtitleItem> data) { var filteredItems = new List <SubtitleItem>(); var previousItem = new SubtitleItem(); foreach (var d in data.Where(d => previousItem.StartTime != d.StartTime || previousItem.EndTime != d.EndTime || previousItem.Text != d.Text)) { previousItem = d; filteredItems.Add(d); } return(filteredItems); }
public string GetStringFor(SubtitleItem item) { var builder = new StringBuilder(); if (item != null && item.Lines != null && item.Lines.Count > 0) { foreach (var line in item.Lines) { builder.Append(line); builder.Append(" "); } // remove the last " " builder.Remove(builder.Length - 1, 1); } return(builder.ToString()); }
private SubtitleItem ParseLine(string line, float frameRate) { var match = Regex.Match(line, LineRegex); if (match.Success && match.Groups.Count > 2) { var startFrame = match.Groups[1].Value; var start = (int)(1000 * double.Parse(startFrame) / frameRate); var endTime = match.Groups[2].Value; var end = (int)(1000 * double.Parse(endTime) / frameRate); var text = match.Groups[match.Groups.Count - 1].Value; var lines = text.Split(_lineSeparators); //... آنچه گذشت|{y:i}RamtinJokar|{y:i}SalAM var nonEmptyLines = lines.Where(l => !string.IsNullOrEmpty(l)).ToList(); if (nonEmptyLines.Count > 0) { for (int i = 0; i < nonEmptyLines.Count; i++) { nonEmptyLines[i] = nonEmptyLines[i].Replace("|", Environment.NewLine); if (nonEmptyLines[i].Contains("{y:i}")) { nonEmptyLines[i] = nonEmptyLines[i].Replace("{y:i}", ""); nonEmptyLines[i] = "<i>" + nonEmptyLines[i] + "</i>"; } if (nonEmptyLines[i].Contains("{Y:b}")) { nonEmptyLines[i] = nonEmptyLines[i].Replace("{Y:b}", ""); nonEmptyLines[i] = "<b>" + nonEmptyLines[i] + "</b>"; } } } var item = new SubtitleItem { Lines = nonEmptyLines, StartTime = start, EndTime = end }; return(item); } else { var message = string.Format("The subtitle file line {0} is " + "not in the micro dvd format. We stop the process.", line); throw new InvalidDataException(message); } }
int compareFunc(double timeSeconds, SubtitleItem item) { int timeMilliSeconds = (int)(timeSeconds * 1000); if (timeMilliSeconds < item.StartTime) { return(-1); } else if (timeMilliSeconds > item.EndTime) { return(1); } else { return(0); } }
private static void PlayAudio(string file, SubtitleItem info) { var start = TimeSpan.FromMilliseconds(info.StartTime).ToString("c"); var end = TimeSpan.FromMilliseconds(info.EndTime).ToString("c"); Console.WriteLine(info.ToString()); var startInfo = new ProcessStartInfo("ffmpeg", $"-hide_banner -loglevel panic -i \"{Path.GetFullPath(file)}\" " + $"-ss {start} " + $"-to {end} " + "-f pulse " + "default" ); startInfo.WindowStyle = ProcessWindowStyle.Hidden; startInfo.CreateNoWindow = true; startInfo.RedirectStandardOutput = false; Process.Start(startInfo); }
public SubtitleItem getSubtitle(double timeSeconds) { rwLock.EnterReadLock(); try { SubtitleItem result = null; if (items.Count == 0) { return(result); } int mid = 0; int low = 0; int high = items.Count - 1; while (low <= high) { mid = (high + low) / 2; int val = compareFunc(timeSeconds, items[mid]); if (val < 0) { high = mid - 1; } else if (val > 0) { low = mid + 1; } else { return(items[mid]); } } return(result); } finally { rwLock.ExitReadLock(); } }
private void SetBestSubtitles() { if (!Monitor.TryEnter(_subtitleSearchLocker, 0)) { // no point in doing this if we are already downloading subtitles, which will do the same thing effectively return; } SubtitleItem bestSubMatch = null; try { if (!_subtitleIsDownloading && Source != null && SubtitleStreams.Any(s => s.Type == SubtitleItem.SubtitleType.File && File.Exists(s.Path))) { // if nothing is downloading and we have file subs in the folder string lcode = Main.SubtitleLanguages.Count > 0 ? Main.SubtitleLanguages[0].Id : ""; for (int i = 0; i < Main.SubtitleLanguages.Count; i++) { // TODO this should care also for EMBEDDED subs bestSubMatch = SubtitleStreams.Where(s => s.Type == SubtitleItem.SubtitleType.File && File.Exists(s.Path)).OrderBy(f => Path.GetFileNameWithoutExtension(f.Path).ToLowerInvariant().StartsWith(Path.GetFileNameWithoutExtension(Source.LocalPath.ToLowerInvariant())) && (lcode == "" || Path.GetFileNameWithoutExtension(f.Path).EndsWith(lcode)) ? 0 : 1).FirstOrDefault(); if (bestSubMatch != null) { break; } } ServiceLocator.GetService <IMainView>().DelayedInvoke(() => { SelectedSubtitle = bestSubMatch; }, 200); return; } } finally { Monitor.Exit(_subtitleSearchLocker); } if (bestSubMatch == null) { DownloadSubtitleForUriAndQueryIMDB(Source); } }
private void AddSubtitle(string path) { if (!File.Exists(path)) { return; } var subtitle = new SubtitleItem(); subtitle.Name = Path.GetFileName(path); subtitle.Type = Path.GetExtension(path).ToUpperInvariant(); subtitle.Path = path; this._subtitles.Add(subtitle); }
// Methods ------------------------------------------------------------------------- public List <SubtitleItem> ParseStream(Stream srtStream, Encoding encoding) { // test if stream if readable and seekable (just a check, should be good) if (!srtStream.CanRead || !srtStream.CanSeek) { var message = string.Format("Stream must be seekable and readable in a subtitles parser. " + "Operation interrupted; isSeekable: {0} - isReadable: {1}", srtStream.CanSeek, srtStream.CanSeek); throw new ArgumentException(message); } // seek the beginning of the stream srtStream.Position = 0; var reader = new StreamReader(srtStream, encoding, true); var items = new List <SubtitleItem>(); var srtSubParts = GetSrtSubTitleParts(reader).ToList(); if (srtSubParts.Any()) { foreach (var srtSubPart in srtSubParts) { var lines = srtSubPart.Split(new string[] { Environment.NewLine }, StringSplitOptions.None) .Select(s => s.Trim()) .Where(l => !string.IsNullOrEmpty(l)) .ToList(); var item = new SubtitleItem(); foreach (var line in lines) { if (item.StartTime == 0 && item.EndTime == 0) { // we look for the timecodes first int startTc; int endTc; var success = TryParseTimecodeLine(line, out startTc, out endTc); if (success) { item.StartTime = startTc; item.EndTime = endTc; } } else { // we found the timecode, now we get the text item.Lines.Add(line); } } if ((item.StartTime != 0 || item.EndTime != 0) && item.Lines.Any()) { // parsing succeeded items.Add(item); } } if (items.Any()) { return(items); } else { throw new ArgumentException("Stream is not in a valid Srt format"); } } else { throw new FormatException("Parsing as srt returned no srt part."); } }
// Methods ------------------------------------------------------------------ public List<SubtitleItem> ParseStream(Stream ssaStream, Encoding encoding) { // test if stream if readable and seekable (just a check, should be good) if (!ssaStream.CanRead || !ssaStream.CanSeek) { var message = string.Format("Stream must be seekable and readable in a subtitles parser. " + "Operation interrupted; isSeekable: {0} - isReadable: {1}", ssaStream.CanSeek, ssaStream.CanSeek); throw new ArgumentException(message); } // seek the beginning of the stream ssaStream.Position = 0; var reader = new StreamReader(ssaStream, encoding, true); var line = reader.ReadLine(); var lineNumber = 1; // read the line until the [Events] section while (line != null && line != EventLine) { line = reader.ReadLine(); lineNumber++; } if (line != null) { // we are at the event section var headerLine = reader.ReadLine(); if (!string.IsNullOrEmpty(headerLine)) { var columnHeaders = headerLine.Split(Separator).Select(head => head.Trim()).ToList(); var startIndexColumn = columnHeaders.IndexOf(StartColumn); var endIndexColumn = columnHeaders.IndexOf(EndColumn); var textIndexColumn = columnHeaders.IndexOf(TextColumn); if (startIndexColumn > 0 && endIndexColumn > 0 && textIndexColumn > 0) { var items = new List<SubtitleItem>(); line = reader.ReadLine(); while (line != null) { if(!string.IsNullOrEmpty(line)) { var columns = line.Split(Separator); var startText = columns[startIndexColumn]; var endText = columns[endIndexColumn]; var textLine = string.Join(",", columns.Skip(textIndexColumn)); var start = ParseSsaTimecode(startText); var end = ParseSsaTimecode(endText); // TODO: split text line? if (start > 0 && end > 0 && !string.IsNullOrEmpty(textLine)) { var item = new SubtitleItem() { StartTime = start, EndTime = end, Lines = new List<string>() { textLine } }; items.Add(item); } } line = reader.ReadLine(); } if (items.Any()) { return items; } else { throw new ArgumentException("Stream is not in a valid Ssa format"); } } else { var message = string.Format("Couldn't find all the necessary columns " + "headers ({0}, {1}, {2}) in header line {3}", StartColumn, EndColumn, TextColumn, headerLine); throw new ArgumentException(message); } } else { var message = string.Format("The header line after the line '{0}' was null -> " + "no need to continue parsing", line); throw new ArgumentException(message); } } else { var message = string.Format("We reached line '{0}' with line number #{1} without finding to " + "Event section ({2})", line, lineNumber, EventLine); throw new ArgumentException(message); } }
/// <summary> /// Parses one line of the .sub file /// /// ex: /// {0}{180}PIRATES OF THE CARIBBEAN|English subtitlez by tHe.b0dY /// </summary> /// <param name="line">The .sub file line</param> /// <param name="frameRate">The frame rate with which the .sub file was created</param> /// <returns>The corresponding SubtitleItem</returns> private SubtitleItem ParseLine(string line, float frameRate) { var match = Regex.Match(line, LineRegex); if (match.Success && match.Groups.Count > 2) { var startFrame = match.Groups[1].Value; var start = (int)(1000 * double.Parse(startFrame) / frameRate); var endTime = match.Groups[2].Value; var end = (int)(1000 * double.Parse(endTime) / frameRate); var text = match.Groups[match.Groups.Count - 1].Value; var lines = text.Split(_lineSeparators); var nonEmptyLines = lines.Where(l => !string.IsNullOrEmpty(l)).ToList(); var item = new SubtitleItem { Lines = nonEmptyLines, StartTime = start, EndTime = end }; return item; } else { var message = string.Format("The subtitle file line {0} is " + "not in the micro dvd format. We stop the process.", line); throw new InvalidDataException(message); } }
// Methods ------------------------------------------------------------------------- public List<SubtitleItem> ParseStream(Stream vttStream, Encoding encoding) { // test if stream if readable and seekable (just a check, should be good) if (!vttStream.CanRead || !vttStream.CanSeek) { var message = string.Format("Stream must be seekable and readable in a subtitles parser. " + "Operation interrupted; isSeekable: {0} - isReadable: {1}", vttStream.CanSeek, vttStream.CanSeek); throw new ArgumentException(message); } // seek the beginning of the stream vttStream.Position = 0; var reader = new StreamReader(vttStream, encoding, true); var items = new List<SubtitleItem>(); var vttSubParts = GetVttSubTitleParts(reader).ToList(); if (vttSubParts.Any()) { foreach (var vttSubPart in vttSubParts) { var lines = vttSubPart.Split(new string[] { Environment.NewLine }, StringSplitOptions.None) .Select(s => s.Trim()) .Where(l => !string.IsNullOrEmpty(l)) .ToList(); var item = new SubtitleItem(); foreach (var line in lines) { if (item.StartTime == 0 && item.EndTime == 0) { // we look for the timecodes first int startTc; int endTc; var success = TryParseTimecodeLine(line, out startTc, out endTc); if (success) { item.StartTime = startTc; item.EndTime = endTc; } } else { // we found the timecode, now we get the text item.Lines.Add(line); } } if ((item.StartTime != 0 || item.EndTime != 0) && item.Lines.Any()) { // parsing succeeded items.Add(item); } } if (items.Any()) { return items; } else { throw new ArgumentException("Stream is not in a valid VTT format"); } } else { throw new FormatException("Parsing as VTT returned no VTT part."); } }