From e627c723e29804e8f6f682bb61032908961f8699 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sat, 23 May 2026 22:41:44 +0200 Subject: Extract attachments in one ffmpeg command when dumping --- .../Attachments/AttachmentExtractor.cs | 147 ++++++++++++++++++++- 1 file changed, 140 insertions(+), 7 deletions(-) (limited to 'MediaBrowser.MediaEncoding') diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index d9cb7a450f..9dd3dcecba 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; using AsyncKeyedLock; @@ -102,13 +104,10 @@ namespace MediaBrowser.MediaEncoding.Attachments && (a.FileName.Contains('/', StringComparison.OrdinalIgnoreCase) || a.FileName.Contains('\\', StringComparison.OrdinalIgnoreCase))); if (shouldExtractOneByOne && !inputFile.EndsWith(".mks", StringComparison.OrdinalIgnoreCase)) { - foreach (var attachment in mediaSource.MediaAttachments) - { - if (!string.Equals(attachment.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase)) - { - await ExtractAttachment(inputFile, mediaSource, attachment, cancellationToken).ConfigureAwait(false); - } - } + await ExtractAllAttachmentsIndividuallyInternal( + inputFile, + mediaSource, + cancellationToken).ConfigureAwait(false); } else { @@ -119,6 +118,140 @@ namespace MediaBrowser.MediaEncoding.Attachments } } + private async Task ExtractAllAttachmentsIndividuallyInternal( + string inputFile, + MediaSourceInfo mediaSource, + CancellationToken cancellationToken) + { + var inputPath = _mediaEncoder.GetInputArgument(inputFile, mediaSource); + + ArgumentException.ThrowIfNullOrEmpty(inputPath); + + var outputFolder = _pathManager.GetAttachmentFolderPath(mediaSource.Id); + if (outputFolder is null) + { + _logger.LogDebug("Skipping attachment extraction for input {InputFile}: MediaSource Id is not a GUID.", inputFile); + return; + } + + using (await _semaphoreLocks.LockAsync(outputFolder, cancellationToken).ConfigureAwait(false)) + { + Directory.CreateDirectory(outputFolder); + + var dumpArgs = new StringBuilder(); + var missingPaths = new List(); + foreach (var attachment in mediaSource.MediaAttachments) + { + if (string.Equals(attachment.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + var indexName = attachment.Index.ToString(CultureInfo.InvariantCulture); + var attachmentPath = _pathManager.GetAttachmentPath(mediaSource.Id, attachment.FileName ?? indexName) + ?? _pathManager.GetAttachmentPath(mediaSource.Id, indexName)!; + if (File.Exists(attachmentPath)) + { + continue; + } + + dumpArgs.AppendFormat( + CultureInfo.InvariantCulture, + "-dump_attachment:{0} \"{1}\" ", + attachment.Index, + EncodingUtils.NormalizePath(attachmentPath)); + missingPaths.Add(attachmentPath); + } + + if (missingPaths.Count == 0) + { + // Skip extraction if all files already exist + return; + } + + var hasVideoOrAudioStream = mediaSource.MediaStreams + .Any(s => s.Type == MediaStreamType.Video || s.Type == MediaStreamType.Audio); + var processArgs = string.Format( + CultureInfo.InvariantCulture, + "{0}{1} -i {2} {3}", + dumpArgs, + inputPath.EndsWith(".concat\"", StringComparison.OrdinalIgnoreCase) ? "-f concat -safe 0" : string.Empty, + inputPath, + hasVideoOrAudioStream ? "-t 0 -f null null" : string.Empty); + + int exitCode; + + using (var process = new Process + { + StartInfo = new ProcessStartInfo + { + Arguments = processArgs, + FileName = _mediaEncoder.EncoderPath, + UseShellExecute = false, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + ErrorDialog = false + }, + EnableRaisingEvents = true + }) + { + _logger.LogInformation("{File} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments); + + process.Start(); + + try + { + await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false); + exitCode = process.ExitCode; + } + catch (OperationCanceledException) + { + process.Kill(true); + exitCode = -1; + } + } + + var failed = false; + + if (exitCode != 0 && (hasVideoOrAudioStream || exitCode != 1)) + { + failed = true; + + foreach (var path in missingPaths) + { + if (!File.Exists(path)) + { + continue; + } + + try + { + _fileSystem.DeleteFile(path); + } + catch (IOException ex) + { + _logger.LogError(ex, "Error deleting extracted attachment {Path}", path); + } + } + } + + if (!failed && missingPaths.Exists(p => !File.Exists(p))) + { + failed = true; + } + + if (failed) + { + _logger.LogError("ffmpeg attachment extraction failed for {InputPath} to {OutputPath}", inputPath, outputFolder); + + throw new InvalidOperationException( + string.Format(CultureInfo.InvariantCulture, "ffmpeg attachment extraction failed for {0} to {1}", inputPath, outputFolder)); + } + + _logger.LogInformation("ffmpeg attachment extraction completed for {InputPath} to {OutputPath}", inputPath, outputFolder); + } + } + private async Task ExtractAllAttachmentsInternal( string inputFile, MediaSourceInfo mediaSource, -- cgit v1.2.3 From 941298ee8108d79bd2f9bc010415103fddf54b0e Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 8 May 2026 21:29:13 +0200 Subject: Write subtitles using SubtitleEdit We've been using SubtitleEdit to parse since 2021 https://github.com/jellyfin/jellyfin/pull/4984 I think it's time we start using it to write too --- MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs | 57 ----- .../Subtitles/ISubtitleWriter.cs | 20 -- MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs | 44 ---- MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs | 49 ---- MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs | 57 ----- .../Subtitles/SubtitleEncoder.cs | 65 ++--- MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs | 60 ----- MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs | 53 ---- .../Subtitles/FilterEventsTests.cs | 282 --------------------- 9 files changed, 35 insertions(+), 652 deletions(-) delete mode 100644 MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs delete mode 100644 MediaBrowser.MediaEncoding/Subtitles/ISubtitleWriter.cs delete mode 100644 MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs delete mode 100644 MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs delete mode 100644 MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs delete mode 100644 MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs delete mode 100644 MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs delete mode 100644 tests/Jellyfin.MediaEncoding.Tests/Subtitles/FilterEventsTests.cs (limited to 'MediaBrowser.MediaEncoding') diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs deleted file mode 100644 index 7d7b80e99d..0000000000 --- a/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Globalization; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using MediaBrowser.Model.MediaInfo; - -namespace MediaBrowser.MediaEncoding.Subtitles -{ - /// - /// ASS subtitle writer. - /// - public partial class AssWriter : ISubtitleWriter - { - [GeneratedRegex(@"\n", RegexOptions.IgnoreCase)] - private static partial Regex NewLineRegex(); - - /// - public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken) - { - using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) - { - var trackEvents = info.TrackEvents; - var timeFormat = @"hh\:mm\:ss\.ff"; - - // Write ASS header - writer.WriteLine("[Script Info]"); - writer.WriteLine("Title: Jellyfin transcoded ASS subtitle"); - writer.WriteLine("ScriptType: v4.00+"); - writer.WriteLine(); - writer.WriteLine("[V4+ Styles]"); - writer.WriteLine("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding"); - writer.WriteLine("Style: Default,Arial,20,&H00FFFFFF,&H00FFFFFF,&H19333333,&H910E0807,0,0,0,0,100,100,0,0,0,1,0,2,10,10,10,1"); - writer.WriteLine(); - writer.WriteLine("[Events]"); - writer.WriteLine("Format: Layer, Start, End, Style, Text"); - - for (int i = 0; i < trackEvents.Count; i++) - { - cancellationToken.ThrowIfCancellationRequested(); - - var trackEvent = trackEvents[i]; - var startTime = TimeSpan.FromTicks(trackEvent.StartPositionTicks).ToString(timeFormat, CultureInfo.InvariantCulture); - var endTime = TimeSpan.FromTicks(trackEvent.EndPositionTicks).ToString(timeFormat, CultureInfo.InvariantCulture); - var text = NewLineRegex().Replace(trackEvent.Text, "\\n"); - - writer.WriteLine( - "Dialogue: 0,{0},{1},Default,{2}", - startTime, - endTime, - text); - } - } - } - } -} diff --git a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleWriter.cs deleted file mode 100644 index dec714121d..0000000000 --- a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleWriter.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.IO; -using System.Threading; -using MediaBrowser.Model.MediaInfo; - -namespace MediaBrowser.MediaEncoding.Subtitles -{ - /// - /// Interface ISubtitleWriter. - /// - public interface ISubtitleWriter - { - /// - /// Writes the specified information. - /// - /// The information. - /// The stream. - /// The cancellation token. - void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken); - } -} diff --git a/MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs deleted file mode 100644 index 1b452b0cec..0000000000 --- a/MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.IO; -using System.Text.Json; -using System.Threading; -using MediaBrowser.Model.MediaInfo; - -namespace MediaBrowser.MediaEncoding.Subtitles -{ - /// - /// JSON subtitle writer. - /// - public class JsonWriter : ISubtitleWriter - { - /// - public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken) - { - using (var writer = new Utf8JsonWriter(stream)) - { - var trackevents = info.TrackEvents; - writer.WriteStartObject(); - writer.WriteStartArray("TrackEvents"); - - for (int i = 0; i < trackevents.Count; i++) - { - cancellationToken.ThrowIfCancellationRequested(); - - var current = trackevents[i]; - writer.WriteStartObject(); - - writer.WriteString("Id", current.Id); - writer.WriteString("Text", current.Text); - writer.WriteNumber("StartPositionTicks", current.StartPositionTicks); - writer.WriteNumber("EndPositionTicks", current.EndPositionTicks); - - writer.WriteEndObject(); - } - - writer.WriteEndArray(); - writer.WriteEndObject(); - - writer.Flush(); - } - } - } -} diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs deleted file mode 100644 index 86f77aa067..0000000000 --- a/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Globalization; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using MediaBrowser.Model.MediaInfo; - -namespace MediaBrowser.MediaEncoding.Subtitles -{ - /// - /// SRT subtitle writer. - /// - public partial class SrtWriter : ISubtitleWriter - { - [GeneratedRegex(@"\\n", RegexOptions.IgnoreCase)] - private static partial Regex NewLineEscapedRegex(); - - /// - public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken) - { - using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) - { - var trackEvents = info.TrackEvents; - - for (int i = 0; i < trackEvents.Count; i++) - { - cancellationToken.ThrowIfCancellationRequested(); - - var trackEvent = trackEvents[i]; - - writer.WriteLine((i + 1).ToString(CultureInfo.InvariantCulture)); - writer.WriteLine( - @"{0:hh\:mm\:ss\,fff} --> {1:hh\:mm\:ss\,fff}", - TimeSpan.FromTicks(trackEvent.StartPositionTicks), - TimeSpan.FromTicks(trackEvent.EndPositionTicks)); - - var text = trackEvent.Text; - - // TODO: Not sure how to handle these - text = NewLineEscapedRegex().Replace(text, " "); - - writer.WriteLine(text); - writer.WriteLine(); - } - } - } - } -} diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs deleted file mode 100644 index b5fd1ed935..0000000000 --- a/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Globalization; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using MediaBrowser.Model.MediaInfo; - -namespace MediaBrowser.MediaEncoding.Subtitles -{ - /// - /// SSA subtitle writer. - /// - public partial class SsaWriter : ISubtitleWriter - { - [GeneratedRegex(@"\n", RegexOptions.IgnoreCase)] - private static partial Regex NewLineRegex(); - - /// - public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken) - { - using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) - { - var trackEvents = info.TrackEvents; - var timeFormat = @"hh\:mm\:ss\.ff"; - - // Write SSA header - writer.WriteLine("[Script Info]"); - writer.WriteLine("Title: Jellyfin transcoded SSA subtitle"); - writer.WriteLine("ScriptType: v4.00"); - writer.WriteLine(); - writer.WriteLine("[V4 Styles]"); - writer.WriteLine("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding"); - writer.WriteLine("Style: Default,Arial,20,&H00FFFFFF,&H00FFFFFF,&H19333333,&H19333333,0,0,0,1,0,2,10,10,10,0,1"); - writer.WriteLine(); - writer.WriteLine("[Events]"); - writer.WriteLine("Format: Layer, Start, End, Style, Text"); - - for (int i = 0; i < trackEvents.Count; i++) - { - cancellationToken.ThrowIfCancellationRequested(); - - var trackEvent = trackEvents[i]; - var startTime = TimeSpan.FromTicks(trackEvent.StartPositionTicks).ToString(timeFormat, CultureInfo.InvariantCulture); - var endTime = TimeSpan.FromTicks(trackEvent.EndPositionTicks).ToString(timeFormat, CultureInfo.InvariantCulture); - var text = NewLineRegex().Replace(trackEvent.Text, "\\n"); - - writer.WriteLine( - "Dialogue: 0,{0},{1},Default,{2}", - startTime, - endTime, - text); - } - } - } - } -} diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index e0c5f3ad39..2dc71d08c4 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -26,7 +26,10 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; +using Nikse.SubtitleEdit.Core.Common; +using Nikse.SubtitleEdit.Core.SubtitleFormats; using UtfUnknown; +using SubtitleFormat = MediaBrowser.Model.MediaInfo.SubtitleFormat; namespace MediaBrowser.MediaEncoding.Subtitles { @@ -72,7 +75,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles private MemoryStream ConvertSubtitles( Stream stream, - string inputFormat, + SubtitleInfo inputInfo, string outputFormat, long startTimeTicks, long endTimeTicks, @@ -83,13 +86,18 @@ namespace MediaBrowser.MediaEncoding.Subtitles try { - var trackInfo = _subtitleParser.Parse(stream, inputFormat); + var subtitle = Subtitle.Parse(stream, Path.GetExtension(inputInfo.Path)); - FilterEvents(trackInfo, startTimeTicks, endTimeTicks, preserveOriginalTimestamps); + FilterEvents(subtitle, startTimeTicks, endTimeTicks, preserveOriginalTimestamps); - var writer = GetWriter(outputFormat); + var formatter = GetWriter(outputFormat); + + var text = formatter.ToText(subtitle, "untitled"); + using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + { + writer.Write(text); + } - writer.Write(trackInfo, ms, cancellationToken); ms.Position = 0; } catch @@ -101,26 +109,24 @@ namespace MediaBrowser.MediaEncoding.Subtitles return ms; } - internal void FilterEvents(SubtitleTrackInfo track, long startPositionTicks, long endTimeTicks, bool preserveTimestamps) + internal void FilterEvents(Subtitle track, long startPositionTicks, long endTimeTicks, bool preserveTimestamps) { // Drop subs that have fully elapsed before the requested start position - track.TrackEvents = track.TrackEvents - .SkipWhile(i => (i.StartPositionTicks - startPositionTicks) < 0 && (i.EndPositionTicks - startPositionTicks) < 0) - .ToArray(); + track.Paragraphs + .RemoveAll(i => (i.StartTime.TimeSpan.Ticks - startPositionTicks) < 0 && (i.EndTime.TimeSpan.Ticks - startPositionTicks) < 0); if (endTimeTicks > 0) { - track.TrackEvents = track.TrackEvents - .TakeWhile(i => i.StartPositionTicks <= endTimeTicks) - .ToArray(); + track.Paragraphs + .RemoveAll(i => i.StartTime.TimeSpan.Ticks > endTimeTicks); } if (!preserveTimestamps) { - foreach (var trackEvent in track.TrackEvents) + foreach (var trackEvent in track.Paragraphs) { - trackEvent.EndPositionTicks = Math.Max(0, trackEvent.EndPositionTicks - startPositionTicks); - trackEvent.StartPositionTicks = Math.Max(0, trackEvent.StartPositionTicks - startPositionTicks); + trackEvent.StartTime = new TimeCode(TimeSpan.FromTicks(Math.Max(0, trackEvent.StartTime.TimeSpan.Ticks - startPositionTicks))); + trackEvent.EndTime = new TimeCode(TimeSpan.FromTicks(Math.Max(0, trackEvent.EndTime.TimeSpan.Ticks - startPositionTicks))); } } } @@ -142,14 +148,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles var subtitleStream = mediaSource.MediaStreams .First(i => i.Type == MediaStreamType.Subtitle && i.Index == subtitleStreamIndex); - var (stream, inputFormat) = await GetSubtitleStream(mediaSource, subtitleStream, cancellationToken) + var (stream, info) = await GetSubtitleStream(mediaSource, subtitleStream, cancellationToken) .ConfigureAwait(false); // Return the original if the same format is being requested // Character encoding was already handled in GetSubtitleStream // ASS is a superset of SSA, skipping the conversion and preserving the styles - if (string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase) - || (string.Equals(inputFormat, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase) + if (string.Equals(info.Format, outputFormat, StringComparison.OrdinalIgnoreCase) + || (string.Equals(info.Format, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase) && string.Equals(outputFormat, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase))) { return stream; @@ -157,11 +163,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles using (stream) { - return ConvertSubtitles(stream, inputFormat, outputFormat, startTimeTicks, endTimeTicks, preserveOriginalTimestamps, cancellationToken); + return ConvertSubtitles(stream, info, outputFormat, startTimeTicks, endTimeTicks, preserveOriginalTimestamps, cancellationToken); } } - private async Task<(Stream Stream, string Format)> GetSubtitleStream( + private async Task<(Stream Stream, SubtitleInfo Info)> GetSubtitleStream( MediaSourceInfo mediaSource, MediaStream subtitleStream, CancellationToken cancellationToken) @@ -170,7 +176,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles var stream = await GetSubtitleStream(fileInfo, cancellationToken).ConfigureAwait(false); - return (stream, fileInfo.Format); + return (stream, fileInfo); } private async Task GetSubtitleStream(SubtitleInfo fileInfo, CancellationToken cancellationToken) @@ -267,43 +273,42 @@ namespace MediaBrowser.MediaEncoding.Subtitles }; } - private bool TryGetWriter(string format, [NotNullWhen(true)] out ISubtitleWriter? value) + private bool TryGetWriter(string format, [NotNullWhen(true)] out Nikse.SubtitleEdit.Core.SubtitleFormats.SubtitleFormat? value) { ArgumentException.ThrowIfNullOrEmpty(format); if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase)) { - value = new AssWriter(); + value = new AdvancedSubStationAlpha(); return true; } if (string.Equals(format, "json", StringComparison.OrdinalIgnoreCase)) { - value = new JsonWriter(); - return true; + throw new NotImplementedException(); } if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase) || string.Equals(format, SubtitleFormat.SUBRIP, StringComparison.OrdinalIgnoreCase)) { - value = new SrtWriter(); + value = new SubRip(); return true; } if (string.Equals(format, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase)) { - value = new SsaWriter(); + value = new SubStationAlpha(); return true; } if (string.Equals(format, SubtitleFormat.VTT, StringComparison.OrdinalIgnoreCase) || string.Equals(format, SubtitleFormat.WEBVTT, StringComparison.OrdinalIgnoreCase)) { - value = new VttWriter(); + value = new WebVTT(); return true; } if (string.Equals(format, SubtitleFormat.TTML, StringComparison.OrdinalIgnoreCase)) { - value = new TtmlWriter(); + value = new TimedText10(); return true; } @@ -311,7 +316,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles return false; } - private ISubtitleWriter GetWriter(string format) + private Nikse.SubtitleEdit.Core.SubtitleFormats.SubtitleFormat GetWriter(string format) { if (TryGetWriter(format, out var writer)) { diff --git a/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs deleted file mode 100644 index ea45f2070a..0000000000 --- a/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.IO; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using MediaBrowser.Model.MediaInfo; - -namespace MediaBrowser.MediaEncoding.Subtitles -{ - /// - /// TTML subtitle writer. - /// - public partial class TtmlWriter : ISubtitleWriter - { - [GeneratedRegex(@"\\n", RegexOptions.IgnoreCase)] - private static partial Regex NewLineEscapeRegex(); - - /// - public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken) - { - // Example: https://github.com/zmalltalker/ttml2vtt/blob/master/data/sample.xml - // Parser example: https://github.com/mozilla/popcorn-js/blob/master/parsers/parserTTML/popcorn.parserTTML.js - - using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) - { - writer.WriteLine(""); - writer.WriteLine(""); - - writer.WriteLine(""); - writer.WriteLine(""); - writer.WriteLine("