Skip to content

Commit

Permalink
fixes MediaBrowser#674 - Support converting subtitles to webvtt
Browse files Browse the repository at this point in the history
  • Loading branch information
LukePulverenti committed Jun 11, 2014
1 parent 67ae275 commit a47912e
Show file tree
Hide file tree
Showing 18 changed files with 726 additions and 39 deletions.
48 changes: 32 additions & 16 deletions MediaBrowser.Api/Library/SubtitleService.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using ServiceStack;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace MediaBrowser.Api.Library
{
[Route("/Videos/{Id}/Subtitles/{Index}", "GET", Summary = "Gets an external subtitle file")]
[Route("/Videos/{Id}/Subtitles/{Index}/Stream.{Format}", "GET", Summary = "Gets subtitles in a specified format (vtt).")]
public class GetSubtitle
{
/// <summary>
Expand All @@ -25,8 +26,14 @@ public class GetSubtitle
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }

[ApiMember(Name = "MediaSourceId", Description = "MediaSourceId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string MediaSourceId { get; set; }

[ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
public int Index { get; set; }

[ApiMember(Name = "Format", Description = "Format", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Format { get; set; }
}

[Route("/Videos/{Id}/Subtitles/{Index}", "DELETE", Summary = "Deletes an external subtitle file")]
Expand Down Expand Up @@ -81,13 +88,13 @@ public class SubtitleService : BaseApiService
{
private readonly ILibraryManager _libraryManager;
private readonly ISubtitleManager _subtitleManager;
private readonly IItemRepository _itemRepo;
private readonly ISubtitleEncoder _subtitleEncoder;

public SubtitleService(ILibraryManager libraryManager, ISubtitleManager subtitleManager, IItemRepository itemRepo)
public SubtitleService(ILibraryManager libraryManager, ISubtitleManager subtitleManager, ISubtitleEncoder subtitleEncoder)
{
_libraryManager = libraryManager;
_subtitleManager = subtitleManager;
_itemRepo = itemRepo;
_subtitleEncoder = subtitleEncoder;
}

public object Get(SearchRemoteSubtitles request)
Expand All @@ -100,21 +107,30 @@ public object Get(SearchRemoteSubtitles request)
}
public object Get(GetSubtitle request)
{
var subtitleStream = _itemRepo.GetMediaStreams(new MediaStreamQuery
if (string.IsNullOrEmpty(request.Format))
{
var item = (Video)_libraryManager.GetItemById(new Guid(request.Id));

Index = request.Index,
ItemId = new Guid(request.Id),
Type = MediaStreamType.Subtitle
var mediaSource = item.GetMediaSources(false)
.First(i => string.Equals(i.Id, request.MediaSourceId ?? request.Id));

}).FirstOrDefault();
var subtitleStream = mediaSource.MediaStreams
.First(i => i.Type == MediaStreamType.Subtitle && i.Index == request.Index);

if (subtitleStream == null)
{
throw new ResourceNotFoundException();
return ToStaticFileResult(subtitleStream.Path);
}

return ToStaticFileResult(subtitleStream.Path);
var stream = GetSubtitles(request).Result;

return ResultFactory.GetResult(stream, Common.Net.MimeTypes.GetMimeType("file." + request.Format));
}

private async Task<Stream> GetSubtitles(GetSubtitle request)
{
var stream = await _subtitleEncoder.GetSubtitles(request.Id, request.MediaSourceId, request.Index, request.Format,
CancellationToken.None);

return stream;
}

public void Delete(DeleteSubtitle request)
Expand All @@ -135,7 +151,7 @@ public object Get(GetRemoteSubtitles request)
{
var result = _subtitleManager.GetRemoteSubtitles(request.Id, CancellationToken.None).Result;

return ResultFactory.GetResult(result.Stream, MimeTypes.GetMimeType("file." + result.Format));
return ResultFactory.GetResult(result.Stream, Common.Net.MimeTypes.GetMimeType("file." + result.Format));
}

public void Post(DownloadRemoteSubtitles request)
Expand Down
5 changes: 5 additions & 0 deletions MediaBrowser.Common/Net/MimeTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ public static string GetMimeType(string path)
return "text/plain";
}

if (ext.Equals(".vtt", StringComparison.OrdinalIgnoreCase))
{
return "text/vtt";
}

throw new ArgumentException("Argument not supported: " + path);
}
}
Expand Down
12 changes: 9 additions & 3 deletions MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
using System;
using System.IO;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace MediaBrowser.Controller.MediaEncoding
{
public interface ISubtitleEncoder
{
Task<Stream> ConvertTextSubtitle(String stream,
Task<Stream> ConvertSubtitles(
Stream stream,
string inputFormat,
string outputFormat,
CancellationToken cancellationToken);

Task<Stream> GetSubtitles(string itemId,
string mediaSourceId,
int subtitleStreamIndex,
string outputFormat,
CancellationToken cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
<Compile Include="Subtitles\ISubtitleWriter.cs" />
<Compile Include="Subtitles\SrtParser.cs" />
<Compile Include="Subtitles\SsaParser.cs" />
<Compile Include="Subtitles\SubtitleEncoder.cs" />
<Compile Include="Subtitles\SubtitleTrackInfo.cs" />
<Compile Include="Subtitles\VttWriter.cs" />
</ItemGroup>
Expand Down
9 changes: 8 additions & 1 deletion MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
using System.IO;
using System.Threading;

namespace MediaBrowser.MediaEncoding.Subtitles
{
public interface ISubtitleParser
{
SubtitleTrackInfo Parse(Stream stream);
/// <summary>
/// Parses the specified stream.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>SubtitleTrackInfo.</returns>
SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken);
}
}
4 changes: 3 additions & 1 deletion MediaBrowser.MediaEncoding/Subtitles/ISubtitleWriter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using System.Threading;

namespace MediaBrowser.MediaEncoding.Subtitles
{
Expand All @@ -12,6 +13,7 @@ public interface ISubtitleWriter
/// </summary>
/// <param name="info">The information.</param>
/// <param name="stream">The stream.</param>
void Write(SubtitleTrackInfo info, Stream stream);
/// <param name="cancellationToken">The cancellation token.</param>
void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken);
}
}
12 changes: 11 additions & 1 deletion MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,35 @@
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;

namespace MediaBrowser.MediaEncoding.Subtitles
{
public class SrtParser : ISubtitleParser
{
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public SubtitleTrackInfo Parse(Stream stream) {
public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken)
{
var trackInfo = new SubtitleTrackInfo();
using ( var reader = new StreamReader(stream))
{
string line;
while ((line = reader.ReadLine()) != null)
{
cancellationToken.ThrowIfCancellationRequested();

if (string.IsNullOrWhiteSpace(line))
{
continue;
}
var subEvent = new SubtitleTrackEvent {Id = line};
line = reader.ReadLine();

if (string.IsNullOrWhiteSpace(line))
{
continue;
}

var time = Regex.Split(line, @"[\t ]*-->[\t ]*");
subEvent.StartPositionTicks = GetTicks(time[0]);
var endTime = time[1];
Expand Down
5 changes: 4 additions & 1 deletion MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;

namespace MediaBrowser.MediaEncoding.Subtitles
{
public class SsaParser : ISubtitleParser
{
private readonly CultureInfo _usCulture = new CultureInfo("en-US");

public SubtitleTrackInfo Parse(Stream stream)
public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken)
{
var trackInfo = new SubtitleTrackInfo();
var eventIndex = 1;
Expand All @@ -24,6 +25,8 @@ public SubtitleTrackInfo Parse(Stream stream)

while ((line = reader.ReadLine()) != null)
{
cancellationToken.ThrowIfCancellationRequested();

if (string.IsNullOrWhiteSpace(line))
{
continue;
Expand Down
Loading

0 comments on commit a47912e

Please sign in to comment.