diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index f2968c6b5c..ed2533f6d4 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -37,6 +37,31 @@ namespace MediaBrowser.Api.Subtitles public int Index { get; set; } } + [Route("/Videos/{Id}/Subtitles", "POST", Summary = "Upload an external subtitle file")] + [Authenticated(Roles = "admin")] + public class PostSubtitle : IReturnVoid + { + /// + /// Gets or sets the id. + /// + /// The id. + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string Id { get; set; } + + [ApiMember(Name = "Language", Description = "Language", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string Language { get; set; } + + [ApiMember(Name = "Format", Description = "Format", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string Format { get; set; } + + [ApiMember(Name = "IsForced", Description = "IsForced", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string IsForced { get; set; } + + [ApiMember(Name = "Data", Description = "Data", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string Data { get; set; } + + } + [Route("/Items/{Id}/RemoteSearch/Subtitles/{Language}", "GET")] [Authenticated] public class SearchRemoteSubtitles : IReturn @@ -270,6 +295,36 @@ namespace MediaBrowser.Api.Subtitles return _subtitleManager.DeleteSubtitles(item, request.Index); } + public void Post(PostSubtitle request) + { + var video = (Video)_libraryManager.GetItemById(request.Id); + + var bytes = Convert.FromBase64String(request.Data); + var memoryStream = new MemoryStream(bytes) + { + Position = 0 + }; + + Task.Run(async () => + { + try + { + await _subtitleManager.UploadSubtitle(video, new SubtitleResponse { + Format = request.Format, + Language = request.Language, + IsForced = Convert.ToBoolean(request.IsForced), + Stream = memoryStream + }).ConfigureAwait(false); + + _providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.High); + } + catch (Exception ex) + { + Logger.LogError(ex, "Error uploading subtitle"); + } + }); + } + public async Task Get(GetRemoteSubtitles request) { var result = await _subtitleManager.GetRemoteSubtitles(request.Id, CancellationToken.None).ConfigureAwait(false); diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs index 39538aacd7..556e2f66c8 100644 --- a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs +++ b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; @@ -50,6 +51,11 @@ namespace MediaBrowser.Controller.Subtitles /// Task DownloadSubtitles(Video video, LibraryOptions libraryOptions, string subtitleId, CancellationToken cancellationToken); + /// + /// Upload new subtitle. + /// + Task UploadSubtitle(Video video, SubtitleResponse response); + /// /// Gets the remote subtitles. /// diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index 127d29c04e..2b55618a8e 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -147,37 +147,11 @@ namespace MediaBrowser.Providers.Subtitles var parts = subtitleId.Split(new[] { '_' }, 2); var provider = GetProvider(parts.First()); - var saveInMediaFolder = libraryOptions.SaveSubtitlesWithMedia; - try { var response = await GetRemoteSubtitles(subtitleId, cancellationToken).ConfigureAwait(false); - using (var stream = response.Stream) - using (var memoryStream = new MemoryStream()) - { - await stream.CopyToAsync(memoryStream).ConfigureAwait(false); - memoryStream.Position = 0; - - var savePaths = new List(); - var saveFileName = Path.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLowerInvariant(); - - if (response.IsForced) - { - saveFileName += ".forced"; - } - - saveFileName += "." + response.Format.ToLowerInvariant(); - - if (saveInMediaFolder) - { - savePaths.Add(Path.Combine(video.ContainingFolderPath, saveFileName)); - } - - savePaths.Add(Path.Combine(video.GetInternalMetadataPath(), saveFileName)); - - await TrySaveToFiles(memoryStream, savePaths).ConfigureAwait(false); - } + await TrySaveSubtitle(video, libraryOptions, response); } catch (RateLimitExceededException) { @@ -196,6 +170,48 @@ namespace MediaBrowser.Providers.Subtitles } } + /// + public async Task UploadSubtitle(Video video, SubtitleResponse response) + { + var libraryOptions = BaseItem.LibraryManager.GetLibraryOptions(video); + + await TrySaveSubtitle(video, libraryOptions, response); + } + + private async Task TrySaveSubtitle( + Video video, + LibraryOptions libraryOptions, + SubtitleResponse response) + { + var saveInMediaFolder = libraryOptions.SaveSubtitlesWithMedia; + + using (var stream = response.Stream) + using (var memoryStream = new MemoryStream()) + { + await stream.CopyToAsync(memoryStream).ConfigureAwait(false); + memoryStream.Position = 0; + + var savePaths = new List(); + var saveFileName = Path.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLowerInvariant(); + + if (response.IsForced) + { + saveFileName += ".forced"; + } + + saveFileName += "." + response.Format.ToLowerInvariant(); + + if (saveInMediaFolder) + { + savePaths.Add(Path.Combine(video.ContainingFolderPath, saveFileName)); + } + + savePaths.Add(Path.Combine(video.GetInternalMetadataPath(), saveFileName)); + + await TrySaveToFiles(memoryStream, savePaths).ConfigureAwait(false); + } + } + private async Task TrySaveToFiles(Stream stream, List savePaths) { Exception exceptionToThrow = null;