Kaynağa Gözat

Support subtitle offset

Luke Pulverenti 11 yıl önce
ebeveyn
işleme
dd7825f6c8

+ 9 - 4
MediaBrowser.Api/Library/SubtitleService.cs

@@ -34,6 +34,9 @@ namespace MediaBrowser.Api.Library
 
         [ApiMember(Name = "Format", Description = "Format", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
         public string Format { get; set; }
+
+        [ApiMember(Name = "StartPositionTicks", Description = "StartPositionTicks", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public long StartPositionTicks { get; set; }
     }
 
     [Route("/Videos/{Id}/Subtitles/{Index}", "DELETE", Summary = "Deletes an external subtitle file")]
@@ -127,10 +130,12 @@ namespace MediaBrowser.Api.Library
 
         private async Task<Stream> GetSubtitles(GetSubtitle request)
         {
-            var stream = await _subtitleEncoder.GetSubtitles(request.Id, request.MediaSourceId, request.Index, request.Format,
-                        CancellationToken.None);
-
-            return stream;
+            return await _subtitleEncoder.GetSubtitles(request.Id, 
+                request.MediaSourceId, 
+                request.Index, 
+                request.Format,
+                request.StartPositionTicks,
+                CancellationToken.None).ConfigureAwait(false);
         }
 
         public void Delete(DeleteSubtitle request)

+ 31 - 37
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -65,30 +65,24 @@ namespace MediaBrowser.Api.Playback
         /// </summary>
         /// <value>The media encoder.</value>
         protected IMediaEncoder MediaEncoder { get; private set; }
-        protected IEncodingManager EncodingManager { get; private set; }
-        protected IDtoService DtoService { get; private set; }
 
         protected IFileSystem FileSystem { get; private set; }
 
-        protected IItemRepository ItemRepository { get; private set; }
         protected ILiveTvManager LiveTvManager { get; private set; }
         protected IDlnaManager DlnaManager { get; private set; }
         protected IChannelManager ChannelManager { get; private set; }
-        protected IHttpClient HttpClient { get; private set; }
+        protected ISubtitleEncoder SubtitleEncoder { get; private set; }
 
         /// <summary>
         /// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
         /// </summary>
-        protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IHttpClient httpClient)
+        protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder)
         {
-            HttpClient = httpClient;
+            SubtitleEncoder = subtitleEncoder;
             ChannelManager = channelManager;
             DlnaManager = dlnaManager;
-            EncodingManager = encodingManager;
             LiveTvManager = liveTvManager;
-            ItemRepository = itemRepository;
             FileSystem = fileSystem;
-            DtoService = dtoService;
             ServerConfigurationManager = serverConfig;
             UserManager = userManager;
             LibraryManager = libraryManager;
@@ -587,7 +581,7 @@ namespace MediaBrowser.Api.Playback
 
                 if (!string.IsNullOrEmpty(state.SubtitleStream.Language))
                 {
-                    var charenc = MediaEncoder.GetSubtitleLanguageEncodingParam(subtitlePath, state.SubtitleStream.Language);
+                    var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath, state.SubtitleStream.Language);
 
                     if (!string.IsNullOrEmpty(charenc))
                     {
@@ -1374,6 +1368,12 @@ namespace MediaBrowser.Api.Playback
                 var path = recording.RecordingInfo.Path;
                 var mediaUrl = recording.RecordingInfo.Url;
 
+                var source = string.IsNullOrEmpty(request.MediaSourceId)
+                    ? recording.GetMediaSources(false).First()
+                    : recording.GetMediaSources(false).First(i => string.Equals(i.Id, request.MediaSourceId));
+
+                mediaStreams = source.MediaStreams;
+
                 if (string.IsNullOrWhiteSpace(path) && string.IsNullOrWhiteSpace(mediaUrl))
                 {
                     var streamInfo = await LiveTvManager.GetRecordingStream(request.Id, cancellationToken).ConfigureAwait(false);
@@ -1453,51 +1453,45 @@ namespace MediaBrowser.Api.Playback
             }
             else
             {
-                var mediaSource = string.IsNullOrWhiteSpace(request.MediaSourceId)
-                    ? item
-                    : LibraryManager.GetItemById(request.MediaSourceId);
+                var hasMediaSources = (IHasMediaSources)item;
+                var mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
+                    ? hasMediaSources.GetMediaSources(false).First()
+                    : hasMediaSources.GetMediaSources(false).First(i => string.Equals(i.Id, request.MediaSourceId));
+
+                mediaStreams = mediaSource.MediaStreams;
 
                 state.MediaPath = mediaSource.Path;
                 state.IsRemote = mediaSource.LocationType == LocationType.Remote;
+                state.InputContainer = mediaSource.Container;
 
-                var video = mediaSource as Video;
-
-                if (video != null)
+                if (item is Video)
                 {
                     state.IsInputVideo = true;
-                    state.VideoType = video.VideoType;
-                    state.IsoType = video.IsoType;
 
-                    state.PlayableStreamFileNames = video.PlayableStreamFileNames == null
-                        ? new List<string>()
-                        : video.PlayableStreamFileNames.ToList();
-
-                    state.DeInterlace = string.Equals(video.Container, "wtv", StringComparison.OrdinalIgnoreCase);
-
-                    if (video.Timestamp.HasValue)
+                    if (mediaSource.VideoType.HasValue)
                     {
-                        state.InputTimestamp = video.Timestamp.Value;
+                        state.VideoType = mediaSource.VideoType.Value;
                     }
 
-                    state.InputContainer = video.Container;
-                }
+                    state.IsoType = mediaSource.IsoType;
 
-                var audio = mediaSource as Audio;
-                if (audio != null)
-                {
-                    state.InputContainer = audio.Container;
+                    //state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList();
+
+                    if (mediaSource.Timestamp.HasValue)
+                    {
+                        state.InputTimestamp = mediaSource.Timestamp.Value;
+                    }
                 }
 
                 state.RunTimeTicks = mediaSource.RunTimeTicks;
             }
 
-            var videoRequest = request as VideoStreamRequest;
-
-            mediaStreams = mediaStreams ?? ItemRepository.GetMediaStreams(new MediaStreamQuery
+            if (string.Equals(state.InputContainer, "wtv", StringComparison.OrdinalIgnoreCase))
             {
-                ItemId = new Guid(string.IsNullOrWhiteSpace(request.MediaSourceId) ? request.Id : request.MediaSourceId)
+                state.DeInterlace = true;
+            }
 
-            }).ToList();
+            var videoRequest = request as VideoStreamRequest;
 
             AttachMediaStreamInfo(state, mediaStreams, videoRequest, url);
 

+ 1 - 1
MediaBrowser.Api/Playback/Hls/BaseHlsService.cs

@@ -25,7 +25,7 @@ namespace MediaBrowser.Api.Playback.Hls
     /// </summary>
     public abstract class BaseHlsService : BaseStreamingService
     {
-        protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient)
+        protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder)
         {
         }
 

+ 1 - 1
MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs

@@ -62,7 +62,7 @@ namespace MediaBrowser.Api.Playback.Hls
 
     public class DynamicHlsService : BaseHlsService
     {
-        public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient)
+        public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder)
         {
         }
 

+ 1 - 1
MediaBrowser.Api/Playback/Hls/VideoHlsService.cs

@@ -56,7 +56,7 @@ namespace MediaBrowser.Api.Playback.Hls
     /// </summary>
     public class VideoHlsService : BaseHlsService
     {
-        public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient)
+        public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder)
         {
         }
 

+ 1 - 1
MediaBrowser.Api/Playback/Progressive/AudioService.cs

@@ -44,7 +44,7 @@ namespace MediaBrowser.Api.Playback.Progressive
     /// </summary>
     public class AudioService : BaseProgressiveStreamingService
     {
-        public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient, imageProcessor)
+        public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder, imageProcessor, httpClient)
         {
         }
 

+ 3 - 3
MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs

@@ -4,11 +4,9 @@ using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.IO;
 using ServiceStack.Web;
 using System;
@@ -25,10 +23,12 @@ namespace MediaBrowser.Api.Playback.Progressive
     public abstract class BaseProgressiveStreamingService : BaseStreamingService
     {
         protected readonly IImageProcessor ImageProcessor;
+        protected readonly IHttpClient HttpClient;
 
-        protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient)
+        protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder)
         {
             ImageProcessor = imageProcessor;
+            HttpClient = httpClient;
         }
 
         /// <summary>

+ 1 - 1
MediaBrowser.Api/Playback/Progressive/VideoService.cs

@@ -61,7 +61,7 @@ namespace MediaBrowser.Api.Playback.Progressive
     /// </summary>
     public class VideoService : BaseProgressiveStreamingService
     {
-        public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient, imageProcessor)
+        public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder, imageProcessor, httpClient)
         {
         }
 

+ 0 - 2
MediaBrowser.Controller/Entities/Audio/Audio.cs

@@ -7,7 +7,6 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.Serialization;
-using System.Threading;
 
 namespace MediaBrowser.Controller.Entities.Audio
 {
@@ -15,7 +14,6 @@ namespace MediaBrowser.Controller.Entities.Audio
     /// Class Audio
     /// </summary>
     public class Audio : BaseItem, 
-        IHasMediaStreams, 
         IHasAlbumArtist, 
         IHasArtist, 
         IHasMusicGenres, 

+ 0 - 10
MediaBrowser.Controller/Entities/IHasMediaStreams.cs

@@ -1,10 +0,0 @@
-
-namespace MediaBrowser.Controller.Entities
-{
-    /// <summary>
-    /// This is essentially a marker interface
-    /// </summary>
-    public interface IHasMediaStreams
-    {
-    }
-}

+ 0 - 1
MediaBrowser.Controller/Entities/Video.cs

@@ -20,7 +20,6 @@ namespace MediaBrowser.Controller.Entities
     /// Class Video
     /// </summary>
     public class Video : BaseItem, 
-        IHasMediaStreams, 
         IHasAspectRatio, 
         IHasTags, 
         ISupportsPlaceHolders,

+ 1 - 1
MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs

@@ -6,7 +6,7 @@ using System.Threading.Tasks;
 
 namespace MediaBrowser.Controller.LiveTv
 {
-    public interface ILiveTvRecording : IHasImages, IHasMediaStreams
+    public interface ILiveTvRecording : IHasImages, IHasMediaSources
     {
         string ServiceName { get; set; }
 

+ 0 - 1
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -128,7 +128,6 @@
     <Compile Include="Entities\IHasImages.cs" />
     <Compile Include="Entities\IHasKeywords.cs" />
     <Compile Include="Entities\IHasMediaSources.cs" />
-    <Compile Include="Entities\IHasMediaStreams.cs" />
     <Compile Include="Entities\IHasMetascore.cs" />
     <Compile Include="Entities\IHasPreferredMetadataLanguage.cs" />
     <Compile Include="Entities\IHasProductionLocations.cs" />

+ 0 - 20
MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs

@@ -41,26 +41,6 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{Stream}.</returns>
         Task<Stream> ExtractVideoImage(string[] inputFiles, InputType type, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
-        
-        /// <summary>
-        /// Extracts the text subtitle.
-        /// </summary>
-        /// <param name="inputFiles">The input files.</param>
-        /// <param name="type">The type.</param>
-        /// <param name="subtitleStreamIndex">Index of the subtitle stream.</param>
-        /// <param name="copySubtitleStream">if set to true, copy stream instead of converting.</param>
-        /// <param name="outputPath">The output path.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        Task ExtractTextSubtitle(string[] inputFiles, InputType type, int subtitleStreamIndex, bool copySubtitleStream, string outputPath, CancellationToken cancellationToken);
-
-        /// <summary>
-        /// Gets the subtitle language encoding parameter.
-        /// </summary>
-        /// <param name="path">The path.</param>
-        /// <param name="language">The language.</param>
-        /// <returns>System.String.</returns>
-        string GetSubtitleLanguageEncodingParam(string path, string language);
 
         /// <summary>
         /// Gets the media info.

+ 33 - 3
MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs

@@ -6,16 +6,46 @@ namespace MediaBrowser.Controller.MediaEncoding
 {
     public interface ISubtitleEncoder
     {
+        /// <summary>
+        /// Converts the subtitles.
+        /// </summary>
+        /// <param name="stream">The stream.</param>
+        /// <param name="inputFormat">The input format.</param>
+        /// <param name="outputFormat">The output format.</param>
+        /// <param name="startTimeTicks">The start time ticks.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task{Stream}.</returns>
         Task<Stream> ConvertSubtitles(
-            Stream stream, 
-            string inputFormat, 
+            Stream stream,
+            string inputFormat,
             string outputFormat,
+            long startTimeTicks,
             CancellationToken cancellationToken);
 
-        Task<Stream> GetSubtitles(string itemId, 
+        /// <summary>
+        /// Gets the subtitles.
+        /// </summary>
+        /// <param name="itemId">The item identifier.</param>
+        /// <param name="mediaSourceId">The media source identifier.</param>
+        /// <param name="subtitleStreamIndex">Index of the subtitle stream.</param>
+        /// <param name="outputFormat">The output format.</param>
+        /// <param name="startTimeTicks">The start time ticks.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task{Stream}.</returns>
+        Task<Stream> GetSubtitles(string itemId,
             string mediaSourceId,
             int subtitleStreamIndex,
             string outputFormat,
+            long startTimeTicks,
             CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Gets the subtitle language encoding parameter.
+        /// </summary>
+        /// <param name="path">The path.</param>
+        /// <param name="language">The language.</param>
+        /// <returns>System.String.</returns>
+        string GetSubtitleFileCharacterSet(string path, string language);
+
     }
 }

+ 0 - 283
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -285,289 +285,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
             return string.Empty;
         }
 
-        /// <summary>
-        /// Gets the subtitle language encoding param.
-        /// </summary>
-        /// <param name="path">The path.</param>
-        /// <param name="language">The language.</param>
-        /// <returns>System.String.</returns>
-        public string GetSubtitleLanguageEncodingParam(string path, string language)
-        {
-            if (GetFileEncoding(path).Equals(Encoding.UTF8))
-            {
-                return string.Empty;
-            }
-
-            switch (language.ToLower())
-            {
-                case "pol":
-                case "cze":
-                case "ces":
-                case "slo":
-                case "slk":
-                case "hun":
-                case "slv":
-                case "srp":
-                case "hrv":
-                case "rum":
-                case "ron":
-                case "rup":
-                case "alb":
-                case "sqi":
-                    return "windows-1250";
-                case "ara":
-                    return "windows-1256";
-                case "heb":
-                    return "windows-1255";
-                case "grc":
-                case "gre":
-                    return "windows-1253";
-                case "crh":
-                case "ota":
-                case "tur":
-                    return "windows-1254";
-                case "rus":
-                    return "windows-1251";
-                case "vie":
-                    return "windows-1258";
-                case "kor":
-                    return "cp949";
-                default:
-                    return "windows-1252";
-            }
-        }
-
-        private static Encoding GetFileEncoding(string srcFile)
-        {
-            // *** Detect byte order mark if any - otherwise assume default
-            var buffer = new byte[5];
-
-            using (var file = new FileStream(srcFile, FileMode.Open))
-            {
-                file.Read(buffer, 0, 5);
-            }
-
-            if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
-                return Encoding.UTF8;
-            if (buffer[0] == 0xfe && buffer[1] == 0xff)
-                return Encoding.Unicode;
-            if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
-                return Encoding.UTF32;
-            if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
-                return Encoding.UTF7;
-
-            // It's ok - anything aside from utf is ok since that's what we're looking for
-            return Encoding.Default;
-        }
-
-        /// <summary>
-        /// Extracts the text subtitle.
-        /// </summary>
-        /// <param name="inputFiles">The input files.</param>
-        /// <param name="type">The type.</param>
-        /// <param name="subtitleStreamIndex">Index of the subtitle stream.</param>
-        /// <param name="copySubtitleStream">if set to true, copy stream instead of converting.</param>
-        /// <param name="outputPath">The output path.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        /// <exception cref="System.ArgumentException">Must use inputPath list overload</exception>
-        public async Task ExtractTextSubtitle(string[] inputFiles, InputType type, int subtitleStreamIndex,
-            bool copySubtitleStream, string outputPath, CancellationToken cancellationToken)
-        {
-            var semaphore = GetLock(outputPath);
-
-            await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
-            try
-            {
-                if (!File.Exists(outputPath))
-                {
-                    await
-                        ExtractTextSubtitleInternal(GetInputArgument(inputFiles, type), subtitleStreamIndex,
-                            copySubtitleStream, outputPath, cancellationToken).ConfigureAwait(false);
-                }
-            }
-            finally
-            {
-                semaphore.Release();
-            }
-        }
-
-        /// <summary>
-        /// Extracts the text subtitle.
-        /// </summary>
-        /// <param name="inputPath">The input path.</param>
-        /// <param name="subtitleStreamIndex">Index of the subtitle stream.</param>
-        /// <param name="copySubtitleStream">if set to true, copy stream instead of converting.</param>
-        /// <param name="outputPath">The output path.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        /// <exception cref="System.ArgumentNullException">inputPath
-        /// or
-        /// outputPath
-        /// or
-        /// cancellationToken</exception>
-        /// <exception cref="System.ApplicationException"></exception>
-        private async Task ExtractTextSubtitleInternal(string inputPath, int subtitleStreamIndex,
-            bool copySubtitleStream, string outputPath, CancellationToken cancellationToken)
-        {
-            if (string.IsNullOrEmpty(inputPath))
-            {
-                throw new ArgumentNullException("inputPath");
-            }
-
-            if (string.IsNullOrEmpty(outputPath))
-            {
-                throw new ArgumentNullException("outputPath");
-            }
-
-            string processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s ass \"{2}\"", inputPath,
-                subtitleStreamIndex, outputPath);
-
-            if (copySubtitleStream)
-            {
-                processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s copy \"{2}\"", inputPath,
-                    subtitleStreamIndex, outputPath);
-            }
-
-            var process = new Process
-            {
-                StartInfo = new ProcessStartInfo
-                {
-                    CreateNoWindow = true,
-                    UseShellExecute = false,
-
-                    RedirectStandardOutput = false,
-                    RedirectStandardError = true,
-
-                    FileName = FFMpegPath,
-                    Arguments = processArgs,
-                    WindowStyle = ProcessWindowStyle.Hidden,
-                    ErrorDialog = false
-                }
-            };
-
-            _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
-
-            var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-sub-extract-" + Guid.NewGuid() + ".txt");
-            Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
-
-            var logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read,
-                true);
-
-            try
-            {
-                process.Start();
-            }
-            catch (Exception ex)
-            {
-                logFileStream.Dispose();
-
-                _logger.ErrorException("Error starting ffmpeg", ex);
-
-                throw;
-            }
-
-            process.StandardError.BaseStream.CopyToAsync(logFileStream);
-
-            var ranToCompletion = process.WaitForExit(60000);
-
-            if (!ranToCompletion)
-            {
-                try
-                {
-                    _logger.Info("Killing ffmpeg subtitle extraction process");
-
-                    process.Kill();
-
-                    process.WaitForExit(1000);
-                }
-                catch (Exception ex)
-                {
-                    _logger.ErrorException("Error killing subtitle extraction process", ex);
-                }
-                finally
-                {
-                    logFileStream.Dispose();
-                }
-            }
-
-            var exitCode = ranToCompletion ? process.ExitCode : -1;
-
-            process.Dispose();
-
-            var failed = false;
-
-            if (exitCode == -1)
-            {
-                failed = true;
-
-                if (File.Exists(outputPath))
-                {
-                    try
-                    {
-                        _logger.Info("Deleting extracted subtitle due to failure: ", outputPath);
-                        File.Delete(outputPath);
-                    }
-                    catch (IOException ex)
-                    {
-                        _logger.ErrorException("Error deleting extracted subtitle {0}", ex, outputPath);
-                    }
-                }
-            }
-            else if (!File.Exists(outputPath))
-            {
-                failed = true;
-            }
-
-            if (failed)
-            {
-                var msg = string.Format("ffmpeg subtitle extraction failed for {0} to {1}", inputPath, outputPath);
-
-                _logger.Error(msg);
-
-                throw new ApplicationException(msg);
-            }
-            else
-            {
-                var msg = string.Format("ffmpeg subtitle extraction completed for {0} to {1}", inputPath, outputPath);
-
-                _logger.Info(msg);
-            }
-
-            await SetAssFont(outputPath).ConfigureAwait(false);
-        }
-
-        /// <summary>
-        /// Sets the ass font.
-        /// </summary>
-        /// <param name="file">The file.</param>
-        /// <returns>Task.</returns>
-        private async Task SetAssFont(string file)
-        {
-            _logger.Info("Setting ass font within {0}", file);
-
-            string text;
-            Encoding encoding;
-
-            using (var reader = new StreamReader(file, detectEncodingFromByteOrderMarks: true))
-            {
-                encoding = reader.CurrentEncoding;
-
-                text = await reader.ReadToEndAsync().ConfigureAwait(false);
-            }
-
-            var newText = text.Replace(",Arial,", ",Arial Unicode MS,");
-
-            if (!string.Equals(text, newText))
-            {
-                using (var writer = new StreamWriter(file, false, encoding))
-                {
-                    writer.Write(newText);
-                }
-            }
-        }
-
         public Task<Stream> ExtractAudioImage(string path, CancellationToken cancellationToken)
         {
             return ExtractImage(new[] { path }, InputType.File, true, null, null, cancellationToken);

+ 1 - 0
MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
+using System.Text;
 using System.Text.RegularExpressions;
 using System.Threading;
 

+ 129 - 5
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs

@@ -47,13 +47,16 @@ namespace MediaBrowser.MediaEncoding.Subtitles
         public async Task<Stream> ConvertSubtitles(Stream stream,
             string inputFormat,
             string outputFormat,
+            long startTimeTicks,
             CancellationToken cancellationToken)
         {
             var ms = new MemoryStream();
 
             try
             {
-                if (string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase))
+                // Return the original without any conversions, if possible
+                if (startTimeTicks == 0 && 
+                    string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase))
                 {
                     await stream.CopyToAsync(ms, 81920, cancellationToken).ConfigureAwait(false);
                 }
@@ -61,6 +64,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 {
                     var trackInfo = await GetTrackInfo(stream, inputFormat, cancellationToken).ConfigureAwait(false);
 
+                    UpdateStartingPosition(trackInfo, startTimeTicks);
+
                     var writer = GetWriter(outputFormat);
 
                     writer.Write(trackInfo, ms, cancellationToken);
@@ -76,10 +81,26 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             return ms;
         }
 
+        private void UpdateStartingPosition(SubtitleTrackInfo track, long startPositionTicks)
+        {
+            if (startPositionTicks == 0) return;
+
+            foreach (var trackEvent in track.TrackEvents)
+            {
+                trackEvent.EndPositionTicks -= startPositionTicks;
+                trackEvent.StartPositionTicks -= startPositionTicks;
+            }
+
+            track.TrackEvents = track.TrackEvents
+                .SkipWhile(i => i.StartPositionTicks < 0 || i.EndPositionTicks < 0)
+                .ToList();
+        }
+
         public async Task<Stream> GetSubtitles(string itemId,
             string mediaSourceId,
             int subtitleStreamIndex,
             string outputFormat,
+            long startTimeTicks,
             CancellationToken cancellationToken)
         {
             var subtitle = await GetSubtitleStream(itemId, mediaSourceId, subtitleStreamIndex, cancellationToken)
@@ -89,7 +110,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             {
                 var inputFormat = subtitle.Item2;
 
-                return await ConvertSubtitles(stream, inputFormat, outputFormat, cancellationToken).ConfigureAwait(false);
+                return await ConvertSubtitles(stream, inputFormat, outputFormat, startTimeTicks, cancellationToken).ConfigureAwait(false);
             }
         }
 
@@ -127,11 +148,36 @@ namespace MediaBrowser.MediaEncoding.Subtitles
 
             var fileInfo = await GetReadableFile(mediaSource.Path, inputFiles, inputType, subtitleStream, cancellationToken).ConfigureAwait(false);
 
-            var stream = File.OpenRead(fileInfo.Item1);
+            var stream = await GetSubtitleStream(fileInfo.Item1, subtitleStream.Language).ConfigureAwait(false);
 
             return new Tuple<Stream, string>(stream, fileInfo.Item2);
         }
 
+        private async Task<Stream> GetSubtitleStream(string path, string language)
+        {
+            if (!string.IsNullOrEmpty(language))
+            {
+                var charset = GetSubtitleFileCharacterSet(path, language);
+
+                if (!string.IsNullOrEmpty(charset))
+                {
+                    using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
+                    {
+                        using (var reader = new StreamReader(fs, Encoding.GetEncoding(charset)))
+                        {
+                            var text = await reader.ReadToEndAsync().ConfigureAwait(false);
+
+                            var bytes = Encoding.UTF8.GetBytes(text);
+
+                            return new MemoryStream(bytes);
+                        }
+                    }
+                }
+            }
+
+            return File.OpenRead(path);
+        }
+
         private async Task<Tuple<string, string>> GetReadableFile(string mediaPath,
             string[] inputFiles,
             InputType type,
@@ -282,10 +328,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 throw new ArgumentNullException("outputPath");
             }
 
+            Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
 
             var encodingParam = string.IsNullOrEmpty(language)
                 ? string.Empty
-                : _mediaEncoder.GetSubtitleLanguageEncodingParam(inputPath, language);
+                : GetSubtitleFileCharacterSet(inputPath, language);
 
             if (!string.IsNullOrEmpty(encodingParam))
             {
@@ -456,7 +503,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 throw new ArgumentNullException("outputPath");
             }
 
-            string processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s ass \"{2}\"", inputPath,
+            Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
+
+            var processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s ass \"{2}\"", inputPath,
                 subtitleStreamIndex, outputPath);
 
             if (copySubtitleStream)
@@ -615,5 +664,80 @@ namespace MediaBrowser.MediaEncoding.Subtitles
 
             return Path.Combine(SubtitleCachePath, prefix, filename);
         }
+
+        /// <summary>
+        /// Gets the subtitle language encoding param.
+        /// </summary>
+        /// <param name="path">The path.</param>
+        /// <param name="language">The language.</param>
+        /// <returns>System.String.</returns>
+        public string GetSubtitleFileCharacterSet(string path, string language)
+        {
+            if (GetFileEncoding(path).Equals(Encoding.UTF8))
+            {
+                return string.Empty;
+            }
+
+            switch (language.ToLower())
+            {
+                case "pol":
+                case "cze":
+                case "ces":
+                case "slo":
+                case "slk":
+                case "hun":
+                case "slv":
+                case "srp":
+                case "hrv":
+                case "rum":
+                case "ron":
+                case "rup":
+                case "alb":
+                case "sqi":
+                    return "windows-1250";
+                case "ara":
+                    return "windows-1256";
+                case "heb":
+                    return "windows-1255";
+                case "grc":
+                case "gre":
+                    return "windows-1253";
+                case "crh":
+                case "ota":
+                case "tur":
+                    return "windows-1254";
+                case "rus":
+                    return "windows-1251";
+                case "vie":
+                    return "windows-1258";
+                case "kor":
+                    return "cp949";
+                default:
+                    return "windows-1252";
+            }
+        }
+
+        private static Encoding GetFileEncoding(string srcFile)
+        {
+            // *** Detect byte order mark if any - otherwise assume default
+            var buffer = new byte[5];
+
+            using (var file = new FileStream(srcFile, FileMode.Open))
+            {
+                file.Read(buffer, 0, 5);
+            }
+
+            if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
+                return Encoding.UTF8;
+            if (buffer[0] == 0xfe && buffer[1] == 0xff)
+                return Encoding.Unicode;
+            if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
+                return Encoding.UTF32;
+            if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
+                return Encoding.UTF7;
+
+            // It's ok - anything aside from utf is ok since that's what we're looking for
+            return Encoding.Default;
+        }
     }
 }

+ 0 - 1
MediaBrowser.Model/Entities/MediaStream.cs

@@ -130,7 +130,6 @@ namespace MediaBrowser.Model.Entities
         /// <value><c>true</c> if this instance is external; otherwise, <c>false</c>.</value>
         public bool IsExternal { get; set; }
 
-        [IgnoreDataMember]
         public bool IsGraphicalSubtitleStream
         {
             get

+ 5 - 12
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -1,5 +1,4 @@
-using System.Threading;
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Configuration;
@@ -30,7 +29,6 @@ namespace MediaBrowser.Server.Implementations.Dto
     {
         private readonly ILogger _logger;
         private readonly ILibraryManager _libraryManager;
-        private readonly IUserManager _userManager;
         private readonly IUserDataManager _userDataRepository;
         private readonly IItemRepository _itemRepo;
 
@@ -41,11 +39,10 @@ namespace MediaBrowser.Server.Implementations.Dto
 
         private readonly Func<IChannelManager> _channelManagerFactory;
 
-        public DtoService(ILogger logger, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IImageProcessor imageProcessor, IServerConfigurationManager config, IFileSystem fileSystem, IProviderManager providerManager, Func<IChannelManager> channelManagerFactory)
+        public DtoService(ILogger logger, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IImageProcessor imageProcessor, IServerConfigurationManager config, IFileSystem fileSystem, IProviderManager providerManager, Func<IChannelManager> channelManagerFactory)
         {
             _logger = logger;
             _libraryManager = libraryManager;
-            _userManager = userManager;
             _userDataRepository = userDataRepository;
             _itemRepo = itemRepo;
             _imageProcessor = imageProcessor;
@@ -993,9 +990,9 @@ namespace MediaBrowser.Server.Implementations.Dto
             if (fields.Contains(ItemFields.MediaStreams))
             {
                 // Add VideoInfo
-                var iHasMediaStreams = item as IHasMediaStreams;
+                var iHasMediaSources = item as IHasMediaSources;
 
-                if (iHasMediaStreams != null)
+                if (iHasMediaSources != null)
                 {
                     List<MediaStream> mediaStreams;
 
@@ -1007,11 +1004,7 @@ namespace MediaBrowser.Server.Implementations.Dto
                     }
                     else
                     {
-                        mediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery
-                        {
-                            ItemId = item.Id
-
-                        }).ToList();
+                        mediaStreams = iHasMediaSources.GetMediaSources(true).First().MediaStreams;
                     }
 
                     dto.MediaStreams = mediaStreams;

+ 1 - 1
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -498,7 +498,7 @@ namespace MediaBrowser.ServerApplication
             ImageProcessor = new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, MediaEncoder);
             RegisterSingleInstance(ImageProcessor);
 
-            DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager, () => ChannelManager);
+            DtoService = new DtoService(Logger, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager, () => ChannelManager);
             RegisterSingleInstance(DtoService);
 
             SessionManager = new SessionManager(UserDataManager, ServerConfigurationManager, Logger, UserRepository, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, ItemRepository, JsonSerializer, this, HttpClient);

+ 4 - 10
MediaBrowser.ServerApplication/LibraryViewer.cs

@@ -46,18 +46,12 @@ namespace MediaBrowser.ServerApplication
 
                 var json = FormatJson(_jsonSerializer.SerializeToString(item));
 
-                if (item is IHasMediaStreams)
+                var hasMediaSources = item as IHasMediaSources;
+                if (hasMediaSources != null)
                 {
-                    var mediaStreams = _itemRepository.GetMediaStreams(new MediaStreamQuery
-                    {
-                        ItemId = item.Id
+                    var sources = hasMediaSources.GetMediaSources(false).ToList();
 
-                    }).ToList();
-
-                    if (mediaStreams.Count > 0)
-                    {
-                        json += "\n\nMedia Streams:\n\n" + FormatJson(_jsonSerializer.SerializeToString(mediaStreams));
-                    }
+                    json += "\n\nMedia Sources:\n\n" + FormatJson(_jsonSerializer.SerializeToString(sources));
                 }
 
                 txtJson.Text = json;