如何在 C# 中移动 SRT (SubRip) 文件中的字幕时间?

问题描述 投票:0回答:2

SRT 是众所周知的字幕文本文件格式。以下是此格式的简单示例:

1
00:05:00,400 --> 00:05:15,300
This is an example of
a subtitle.

2
00:05:16,400 --> 00:05:25,300
This is an example of
a subtitle - 2nd subtitle.

有时,不幸的是,下载的字幕文件与购买的电影不同步。如何用 C# 编写一个简单的实用程序,将时间平移应用于现有的字幕文件?

c# subtitle srt
2个回答
0
投票

以下 .NET 8 控制台应用程序演示了在 .srt 文件上应用时移的最小工作版本:

var seconds = TimeSpan.FromSeconds(3);
string inputFile = @"d:\how to train your dragon.srt";
string outputFile = @"d:\how to train your dragon shifted.srt";

File.WriteAllLines(outputFile,
    from line in File.ReadAllLines(inputFile)
    let duration = Duration.ParseOrNull(line)
    select duration is null ? line : duration.Shift(seconds).ToString());

public record Duration(TimeSpan Start, TimeSpan End)
{
    private const string FormatString = @"hh\:mm\:ss\,fff";
    private static readonly System.Globalization.CultureInfo Culture = new("en");

    public static Duration? ParseOrNull(string line)
    {
        if (!line.Contains(" --> ")) return null;

        string[] duration = line.Split(new[] { " --> " }, StringSplitOptions.None);

        return new Duration(
            Start: TimeSpan.ParseExact(duration[0], FormatString, Culture),
            End: TimeSpan.ParseExact(duration[1], FormatString, Culture));
    }

    public Duration Shift(TimeSpan shift) => new(Start.Add(shift), End.Add(shift));
    public override string ToString() => $"{ToDuration(Start)} --> {ToDuration(End)}";
    private static string ToDuration(TimeSpan value) => value.ToString(FormatString);
}

缺少示例


0
投票

以下类表示具有

Parse
Shift
ToLines
方法的单个(多行)字幕条目:

[DebuggerDisplay("{Number} {Start} --> {End} {Lines[0]}")]
public sealed class Subtitle
{
    private const string FormatString = @"hh\:mm\:ss\,fff";

    public Subtitle(int number, TimeSpan start, TimeSpan end, string[] lines)
    {
        Number = number;
        Start = start;
        End = end;
        Lines = lines;
    }

    public int Number { get; }
    public TimeSpan Start { get; }
    public TimeSpan End { get; }
    public string[] Lines { get; }

    public static IEnumerable<Subtitle> Parse(string[] lines)
    {
        int index = 0;

        while (index < lines.Length)
        {
            string numberLine = lines[index++];
            string durationLine = lines[index++];
            var subtitleLines = new List<string>();

            string subtitleLine = lines[index++];

            do
            {
                subtitleLines.Add(subtitleLine);

                subtitleLine = index < lines.Length ? lines[index++] : "";
            }
            while (subtitleLine != "");

            var subtitle = Subtitle.Parse(numberLine, durationLine, subtitleLines);

            yield return subtitle;
        }
    }

    private static Subtitle Parse(string numberLine, string durationLine, IEnumerable<string> subtitleLines)
    {
        int number = int.Parse(numberLine);

        string[] duration = durationLine.Split(new[] { " --> " }, StringSplitOptions.None);

        TimeSpan start = TimeSpan.ParseExact(duration[0], FormatString, CultureInfo.InvariantCulture);
        TimeSpan end = TimeSpan.ParseExact(duration[1], FormatString, CultureInfo.InvariantCulture);

        return new Subtitle(number, start, end, subtitleLines.ToArray());
    }

    public Subtitle Shift(TimeSpan duration) =>
        new Subtitle(this.Number, this.Start.Add(duration), this.End.Add(duration), this.Lines);

    public IEnumerable<string> ToLines()
    {
        yield return this.Number.ToString();

        yield return $"{this.ToDuration(this.Start)} --> {this.ToDuration(this.End)}";

        foreach (var line in this.Lines)
        {
            yield return line;
        }

        yield return "";
    }

    private string ToDuration(TimeSpan value) => value.ToString(FormatString);
}

它可以在以下 .NET 4.8 控制台应用程序中使用:

static void Main(string[] args)
{
    if (args.Length != 3 ||
        !double.TryParse(args.Last(), NumberStyles.Number, CultureInfo.InvariantCulture,
            out var seconds))
    {
        Console.WriteLine("Use: shiftsrt [inputfile] [outputfile] [seconds]");
        Console.WriteLine("Example shiftsrt input.srt output.srt +1.3");
        Console.WriteLine("Example shiftsrt input.srt output.srt -4.0");
        return;
    }

    string inputFile = args[0];
    string outputFile = args[1];

    if (!File.Exists(inputFile))
    {
        Console.WriteLine($"File not found: {inputFile}.");
        return;
    }

    if (File.Exists(outputFile))
    {
        Console.WriteLine($"File already exists: {outputFile}.");
        return;
    }

    var subtitles = Subtitle.Parse(File.ReadAllLines(inputFile)).ToArray();

    var shiftedSubtitles =
        from subtitle in subtitles
        select subtitle.Shift(TimeSpan.FromSeconds(seconds));

    var shiftedSubtitleLines = (
            from subtitle in shiftedSubtitles
            from line in subtitle.ToLines()
            select line)
            .ToArray();

    File.WriteAllLines(outputFile, shiftedSubtitleLines);
}
© www.soinside.com 2019 - 2024. All rights reserved.