using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace FfmpegChapterTool
static readonly TimeSpan _videoLength = new TimeSpan(0, 6, 0);
static readonly string _example = @"
static readonly Regex _regex = new Regex(@"((\d{2}):(\d{2}):(\d{2}))\s+-\s+(.*)", RegexOptions.Compiled);
record ParsedLine(string Title, TimeSpan Timestamp, string RawTimeStampValue);
static bool TryParseLine(string input, out ParsedLine result)
var match = _regex.Match(input);
if (!match.Success || match.Groups.Count < 5)
var title = match.Groups[5].Value;
var rawTimeStampValue = match.Groups[1].Value;
if (!TimeSpan.TryParse(rawTimeStampValue, out TimeSpan parsedTimestamp))
result = new ParsedLine(title, parsedTimestamp, rawTimeStampValue);
static IEnumerable<ParsedLine> ReadFile(string path)
var fileStream = File.OpenRead(path);
return ReadStream(fileStream);
static IEnumerable<ParsedLine> ReadString(string raw, Encoding encoding)
var memoryStream = new MemoryStream(encoding.GetBytes(raw ?? string.Empty));
return ReadStream(memoryStream);
static IEnumerable<ParsedLine> ReadStream(Stream stream)
using var streamReader = new StreamReader(stream);
string line = string.Empty;
while ((line = streamReader.ReadLine()) != null)
if (TryParseLine(line, out ParsedLine result))
static string GetChapter(int chapterNum, ParsedLine parsedLine, ParsedLine ParsedLineNext) => $@"# Chapter {chapterNum} - {parsedLine.Title}
# Chapter {chapterNum} starts at {parsedLine.RawTimeStampValue}
START={parsedLine.Timestamp.TotalSeconds * 1000}
# Chapter {chapterNum} ends at {ParsedLineNext.RawTimeStampValue}
END={ParsedLineNext.Timestamp.TotalSeconds * 1000 - 1}
static void Main(string[] args)
var results = ReadString(_example, Encoding.UTF8).ToList();
for (int i = 0; i < results.Count - 1; i++)
var line = GetChapter(i + 1, results[i], results[i + 1]);
var finalLine = GetChapter(results.Count, results.Last(), new ParsedLine("End", _videoLength, _videoLength.ToString()));
Console.WriteLine(finalLine);