📄
src/Infrastructure/Ffmpeg/SubtitleReader.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
using System; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using FFMpegCore; using FFMpegCore.Pipes; using Microsoft.Extensions.Logging; using Slopper.Domain; using SubtitlesParserV2; namespace Slopper.Infrastructure.Ffmpeg; internal sealed class SubtitleReader(ILogger<SubtitleReader> logger) : ISubtitleReader { public async Task<SubtitleEntry[]> ReadSubtitles(MediaItem media, CancellationToken cancellationToken) => media.Subtitles switch { Subtitles.External(var path) => ReadSubtitles(path), Subtitles.Embedded(var index) => await ReadSubtitles(media.Path, index), _ => throw new ArgumentException("Unknown subtitle type"), }; private static SubtitleEntry[] ReadSubtitles(string path) { using var stream = File.OpenRead(path); return ReadSubtitles(stream); } private async Task<SubtitleEntry[]> ReadSubtitles(string path, int index) { using var stream = new MemoryStream(); var args = FFMpegArguments .FromFileInput(path) .OutputToPipe( new StreamPipeSink(stream), addArguments: options => options.SelectStream(index).ForceFormat("srt") ); using (Tracing.StartReadSubtitles(path, index)) { logger.LogInformation("Running ffmpeg {FfmpegArguments}", args.Arguments); await args.ProcessAsynchronously(); } stream.Position = 0; return ReadSubtitles(stream); } private static SubtitleEntry[] ReadSubtitles(Stream stream) { var result = SubtitleParser.ParseStream(stream) ?? throw new Exception("Cannot parse subtitles."); return [ .. result.Subtitles.Select(s => new SubtitleEntry( [.. s.Lines], TimeSpan.FromMilliseconds(s.StartTime), TimeSpan.FromMilliseconds(s.EndTime - s.StartTime) )), ]; } }