public InfoCombined(InfoLine subs1, InfoLine subs2, bool active) { this.subs1 = subs1; this.subs2 = subs2; this.active = active; this.onlyNeededForContext = false; }
/// <summary> /// Create a line info object based on the given parameters. /// </summary> private InfoLine createLineInfo(string lineText, DateTime startTime, DateTime endTime) { lineText = lineText.Replace("\r\n", " "); lineText = lineText.Replace("\r", " "); lineText = lineText.Replace("\n", " "); lineText = lineText.Replace("\t", " "); lineText = lineText.Trim(); InfoLine info = new InfoLine(startTime, endTime, lineText); return(info); }
/// <summary> /// Create a line info object based on the given parameters. /// </summary> private InfoLine createLineInfo(string lineText, string rawStartTime, string rawEndTime) { DateTime startTime = parseTime(rawStartTime); DateTime endTime = parseTime(rawEndTime); lineText = lineText.Replace("\t", " "); lineText = Regex.Replace(lineText, "</?[ibuIBU]>", "").Trim(); InfoLine info = new InfoLine(startTime, endTime, lineText); return(info); }
/// <summary> /// Given a line from Subs1, get the closest matching line from a list of Subs2 lines based on timestamp. /// May return null if Subs1 RemoveNoCounterpart is set and there is no obvious Subs2 counterpart. /// </summary> private InfoCombined getCombinedOverlap(InfoLine subs1LineInfo, List <InfoLine> subs2LineInfos) { InfoLine bestMatch = new InfoLine(); double bestOverlap = -999999999999999999.0; int stopCount = 0; // Compare the given Subs1 against each of the Subs2 lines foreach (InfoLine info in subs2LineInfos) { // How well did these lines overlap (1.0 is the max) double overlap = UtilsSubs.getOverlap(subs1LineInfo.StartTime, subs1LineInfo.EndTime, info.StartTime, info.EndTime); // Did the current pair of lines overlap more than any pair thus far? if (overlap >= bestOverlap) { bestMatch.Actor = info.Actor; bestMatch.EndTime = info.EndTime; bestMatch.StartTime = info.StartTime; bestMatch.Text = info.Text; bestOverlap = overlap; stopCount = 0; // Reset } else { stopCount++; // Stop trying to find matches after a while if (stopCount == 20) { break; } } } // If no overlap, don't use provided Subs1 line if (Settings.Instance.Subs[0].RemoveNoCounterpart && bestOverlap < 0.0) { return(null); } return(new InfoCombined(subs1LineInfo, bestMatch)); }
/// <summary> /// Given the index of the first line of a partial sentence, /// 1) Find the index of the last line of the partial sentence. /// 2) Combine all lines between the first and last lines. /// </summary> private int findEndLineOfCurrentSentence(List <InfoLine> infoLines, int startLineIdx, string continuationChars, out InfoLine combinedLines) { combinedLines = infoLines[startLineIdx]; int lineIdx = startLineIdx + 1; // If given the last line, we can't go any further if (lineIdx >= infoLines.Count) { return(startLineIdx); } // Find the end line for (; lineIdx < infoLines.Count; lineIdx++) { InfoLine curLine = infoLines[lineIdx]; if (curLine.Text.Length == 0) { continue; } string lastCharInLine = curLine.Text[curLine.Text.Length - 1].ToString(); combinedLines.Text += " " + curLine.Text; combinedLines.EndTime = curLine.EndTime; // If we have found the end line of the current sentence if (!continuationChars.Contains(lastCharInLine)) { break; } } return(lineIdx); }
/// <summary> /// Combine lines if the last character of the line indicates that it should be joined with the next line. /// Example: /// /// LineA, /// LineB. /// /// If comma is the character that indicates a partial line, then the above lines would be combined as: /// /// LineA, LineB. /// /// continuationChars is a string containing all of the characters that can indicate a partial line. /// Example: ",、 →" /// /// </summary> private List <InfoLine> combinePartialLinesIntoSentence(List <InfoLine> infoLines, string continuationChars) { if (continuationChars.Length == 0 || infoLines.Count == 0) { return(infoLines); } List <InfoLine> outInfoLines = new List <InfoLine>(); for (int lineIdx = 0; lineIdx < infoLines.Count; lineIdx++) { InfoLine curLine = infoLines[lineIdx]; if (curLine.Text.Length == 0) { continue; } string lastCharInLine = curLine.Text[curLine.Text.Length - 1].ToString(); // If this is a partial line of a sentence, combine if (continuationChars.Contains(lastCharInLine)) { InfoLine combinedLines; int endIdx = findEndLineOfCurrentSentence(infoLines, lineIdx, continuationChars, out combinedLines); outInfoLines.Add(combinedLines); lineIdx = endIdx; } else { outInfoLines.Add(curLine); } } return(outInfoLines); }
/// <summary> /// Parse the subtitle file and return a list of lines. /// </summary> override public List <InfoLine> parse() { List <InfoLine> lineInfos = new List <InfoLine>(2000); SubtitleCreator.SUP.reset(); SubtitleCreator.SUP sup = SubtitleCreator.SUP.Instance; // Load in the .idx/.sub file (takes a while) sup.Filename = this.File; if (Settings.Instance.VobSubColors.Enabled) { sup.ReadSUP(this.Stream, Settings.Instance.VobSubColors.Colors, Settings.Instance.VobSubColors.TransparencyEnabled); } else { sup.ReadSUP(this.Stream, null, null); } UtilsName name = new UtilsName(Settings.Instance.DeckName, 0, 0, new DateTime(), Settings.Instance.VideoClips.Size.Width, Settings.Instance.VideoClips.Size.Height); for (int i = 0; i < sup.GetNoOfSubtitles(); i++) { DateTime startTime = sup.GetStartTime(i); DateTime endTime = sup.GetEndTime(i); // Used for the filename and the desision to save the image file. // Not used to set the time of the InfoLine because the shift is // also applied in WorkerSubs DateTime shiftedStartTime = sup.GetStartTime(i); DateTime shiftedEndTime = sup.GetEndTime(i); if (Settings.Instance.TimeShiftEnabled) { shiftedStartTime = UtilsSubs.shiftTiming(shiftedStartTime, Settings.Instance.Subs[SubsNum - 1].TimeShift); shiftedEndTime = UtilsSubs.shiftTiming(shiftedEndTime, Settings.Instance.Subs[SubsNum - 1].TimeShift); } string bitmapFile = string.Format("{0}_{1:000.}_Stream_{2:00.}_Subs{3}_{4:000.}.{5:00.}.{6:00.}-{7:000.}.{8:00.}.{9:00.}.png", Settings.Instance.DeckName, this.Episode, this.Stream, this.SubsNum, (int)shiftedStartTime.TimeOfDay.TotalMinutes, (int)shiftedStartTime.TimeOfDay.Seconds, (int)(shiftedStartTime.TimeOfDay.Milliseconds * 0.1), (int)shiftedEndTime.TimeOfDay.TotalMinutes, (int)shiftedEndTime.TimeOfDay.Seconds, (int)(shiftedEndTime.TimeOfDay.Milliseconds * 0.1)); DateTime spanStart = Settings.Instance.SpanStart; DateTime spanEnd = Settings.Instance.SpanEnd; // Create a image file for each line of dialog if ((this.WorkerVars.ProcessingType == WorkerVars.SubsProcessingType.Preview) || // Always save the image when previewing (!Settings.Instance.SpanEnabled) || // Always save the image when span is not enabled ((shiftedStartTime >= spanStart) && (shiftedEndTime <= spanEnd))) // When span is enabled, only save the images that are within the span { string imageSavePath = Path.Combine(this.WorkerVars.MediaDir, bitmapFile); sup.GetBitmap(i).Save(imageSavePath, System.Drawing.Imaging.ImageFormat.Png); } string prefixStr = name.createName(ConstantSettings.SrsVobsubFilenamePrefix, 0, 0, new DateTime(), new DateTime(), "", ""); string suffixStr = name.createName(ConstantSettings.SrsVobsubFilenameSuffix, 0, 0, new DateTime(), new DateTime(), "", ""); // Set the line of dialog to the bitmap file string text = String.Format("{0}{1}{2}", prefixStr, // {0} bitmapFile, // {1} suffixStr); // {2} InfoLine info = new InfoLine(startTime, endTime, text); lineInfos.Add(info); } lineInfos.Sort(); return(lineInfos); }
/// <summary> /// Parse the subtitle file and return a list of lines. /// </summary> override public List <InfoLine> parse() { List <string> rawLines = new List <string>(2000); List <InfoLine> lineInfos = new List <InfoLine>(2000); StreamReader subFile = new StreamReader(this.File, this.SubsEncoding); string fileLine; // Store all of the file's lines in a list while ((fileLine = subFile.ReadLine()) != null) { rawLines.Add(fileLine.Trim()); } subFile.Close(); // Get the regex to use with the dialog lines string assDialogRegex = getAssDialogRegex(rawLines); // Extract info from each dialog line foreach (string rawLine in rawLines) { Match lineMatch = Regex.Match(rawLine, assDialogRegex, RegexOptions.IgnoreCase | RegexOptions.Compiled); if (!lineMatch.Success) { continue; } string rawStartTime = lineMatch.Groups["StartTime"].ToString().Trim(); string rawEndTime = lineMatch.Groups["EndTime"].ToString().Trim(); string actor = lineMatch.Groups["Name"].ToString().Trim(); string text = lineMatch.Groups["Text"].ToString().Trim(); // Don't parse styled lines if (Settings.Instance.Subs[SubsNum - 1].RemoveStyledLines && text.StartsWith("{")) { continue; } if ((this.WorkerVars != null) && (this.WorkerVars.ProcessingType != WorkerVars.SubsProcessingType.Dueling)) { // Remove ass-style newlines ("\N") text = text.Replace("\\N", " "); text = text.Replace("\\n", " "); } // Remove styling embedded within lines text = Regex.Replace(text, "{.*?}", ""); // Remove tabs text = Regex.Replace(text, "\t", " ").Trim(); if (text == "") { // No text - try next line continue; } DateTime startTime = parseTime(rawStartTime); DateTime endTime = parseTime(rawEndTime); InfoLine info = new InfoLine(startTime, endTime, text, actor); lineInfos.Add(info); } // Since the dialog lines don't have to be in chronological order, sort by the start time lineInfos.Sort(); return(lineInfos); }
/// <summary> /// Parse the subtitle file and return a list of lines. /// </summary> override public List <InfoLine> parse() { List <string> rawLines = new List <string>(100); List <InfoLine> lineInfos = new List <InfoLine>(100); StreamReader subFile = new StreamReader(this.File, this.SubsEncoding); string fileLine; // Store all of the file's lines in a list while ((fileLine = subFile.ReadLine()) != null) { rawLines.Add(fileLine.Trim()); } subFile.Close(); // Extract info from each dialog line foreach (string rawLine in rawLines) { string curLine = rawLine; MatchCollection timestampMatches = Regex.Matches(curLine, @"\[(?<Min>\d\d):(?<Sec>\d\d)\.(?<HSec>\d\d)\]", RegexOptions.IgnoreCase | RegexOptions.Compiled); string text = Regex.Replace(curLine, @"\[(?<Min>\d\d):(?<Sec>\d\d)\.(?<HSec>\d\d)\]", "", RegexOptions.Compiled).Trim(); foreach (Match timestampMatch in timestampMatches) { if (!timestampMatch.Success) { continue; } int startTimeMin = Int32.Parse(timestampMatch.Groups["Min"].ToString().Trim()); int startTimeSec = Int32.Parse(timestampMatch.Groups["Sec"].ToString().Trim()); int startTimeHSec = Int32.Parse(timestampMatch.Groups["HSec"].ToString().Trim()); DateTime startTime = new DateTime(); startTime = startTime.AddMinutes(startTimeMin); startTime = startTime.AddSeconds(startTimeSec); startTime = startTime.AddMilliseconds(startTimeHSec * 10); DateTime endTime = new DateTime(); text = text.Replace("\t", " ").Trim(); InfoLine info = new InfoLine(startTime, endTime, text); lineInfos.Add(info); } } // Since the dialog lines don't have to be in chronological order, sort by the start time lineInfos.Sort(); // Fill-in the endtimes for (int lineIdx = 0; lineIdx < lineInfos.Count; lineIdx++) { InfoLine infoLineCur = lineInfos[lineIdx]; InfoLine infoLineNext = lineInfos[Math.Min(lineInfos.Count - 1, lineIdx + 1)]; infoLineCur.EndTime = infoLineNext.StartTime; } // Make the endtime of the final lyric an arbitrary 10 seconds long lineInfos[lineInfos.Count - 1].EndTime = new DateTime().AddSeconds(lineInfos[lineInfos.Count - 1].StartTime.TimeOfDay.TotalSeconds + 10); // Remove lines that don't contains lyrics for (int lineIdx = lineInfos.Count - 1; lineIdx >= 0; lineIdx--) { InfoLine infoLineCur = lineInfos[lineIdx]; // Remove blank lines // Remove lines with the same start and end time because it is probably non-lyric meta information // Remove line with a colon because it is probably non-lyric meta information // remove lines with a website because it is probably just an advertisement if ((infoLineCur.Text.Trim().Length == 0) || (infoLineCur.StartTime.TimeOfDay.TotalMilliseconds == infoLineCur.EndTime.TimeOfDay.TotalMilliseconds) || (infoLineCur.Text.Contains(":")) || (infoLineCur.Text.Contains(":")) || (infoLineCur.Text.Contains("www."))) { lineInfos.RemoveAt(lineIdx); } } return(lineInfos); }