diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs
new file mode 100644
index 000000000..0d1cf6e25
--- /dev/null
+++ b/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs
@@ -0,0 +1,54 @@
+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 class AssWriter : ISubtitleWriter
+ {
+ ///
+ 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 = Regex.Replace(trackEvent.Text, @"\n", "\\n", RegexOptions.IgnoreCase);
+
+ writer.WriteLine(
+ "Dialogue: 0,{0},{1},Default,{2}",
+ startTime,
+ endTime,
+ text);
+ }
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs
new file mode 100644
index 000000000..6761cd309
--- /dev/null
+++ b/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs
@@ -0,0 +1,54 @@
+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 class SsaWriter : ISubtitleWriter
+ {
+ ///
+ 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 = Regex.Replace(trackEvent.Text, @"\n", "\\n", RegexOptions.IgnoreCase);
+
+ 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 49bc2d775..b9b8a89eb 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -283,6 +283,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
private bool TryGetWriter(string format, [NotNullWhen(true)] out ISubtitleWriter? value)
{
+ if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase))
+ {
+ value = new AssWriter();
+ return true;
+ }
+
if (string.IsNullOrEmpty(format))
{
throw new ArgumentNullException(nameof(format));
@@ -294,12 +300,18 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return true;
}
- if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase) || string.Equals(format,SubtitleFormat.SUBRIP, StringComparison.OrdinalIgnoreCase))
{
value = new SrtWriter();
return true;
}
+ if (string.Equals(format, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase))
+ {
+ value = new SsaWriter();
+ return true;
+ }
+
if (string.Equals(format, SubtitleFormat.VTT, StringComparison.OrdinalIgnoreCase))
{
value = new VttWriter();
diff --git a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
index 9bc5c31f6..85de91694 100644
--- a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
+++ b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
@@ -5,6 +5,7 @@ namespace MediaBrowser.Model.MediaInfo
public static class SubtitleFormat
{
public const string SRT = "srt";
+ public const string SUBRIP = "subrip";
public const string SSA = "ssa";
public const string ASS = "ass";
public const string VTT = "vtt";