diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index a0ecf7c560..6973a233af 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -1,5 +1,4 @@
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index c2b393fc64..1d83009682 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -101,9 +101,9 @@ namespace MediaBrowser.Api.Playback
         /// </summary>
         /// <param name="outputPath">The output path.</param>
         /// <param name="state">The state.</param>
-        /// <param name="performSubtitleConversions">if set to <c>true</c> [perform subtitle conversions].</param>
+        /// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
         /// <returns>System.String.</returns>
-        protected abstract string GetCommandLineArguments(string outputPath, StreamState state, bool performSubtitleConversions);
+        protected abstract string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding);
 
         /// <summary>
         /// Gets the type of the transcoding job.
@@ -478,12 +478,10 @@ namespace MediaBrowser.Api.Playback
         /// </summary>
         /// <param name="state">The state.</param>
         /// <param name="outputVideoCodec">The output video codec.</param>
-        /// <param name="performTextSubtitleConversion">if set to <c>true</c> [perform text subtitle conversion].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>System.String.</returns>
         protected string GetOutputSizeParam(StreamState state,
             string outputVideoCodec,
-            bool performTextSubtitleConversion,
             CancellationToken cancellationToken)
         {
             // http://sonnati.wordpress.com/2012/10/19/ffmpeg-the-swiss-army-knife-of-internet-streaming-part-vi/
@@ -496,7 +494,7 @@ namespace MediaBrowser.Api.Playback
 
             if (state.SubtitleStream != null && !state.SubtitleStream.IsGraphicalSubtitleStream)
             {
-                assSubtitleParam = GetTextSubtitleParam(state, performTextSubtitleConversion, cancellationToken);
+                assSubtitleParam = GetTextSubtitleParam(state, cancellationToken);
                 copyTsParam = " -copyts";
             }
 
@@ -574,103 +572,41 @@ namespace MediaBrowser.Api.Playback
         /// Gets the text subtitle param.
         /// </summary>
         /// <param name="state">The state.</param>
-        /// <param name="performConversion">if set to <c>true</c> [perform conversion].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>System.String.</returns>
         protected string GetTextSubtitleParam(StreamState state,
-            bool performConversion,
             CancellationToken cancellationToken)
         {
-            var path = state.SubtitleStream.IsExternal ?
-                GetConvertedAssPath(state.SubtitleStream, performConversion, cancellationToken) :
-                GetExtractedAssPath(state, performConversion, cancellationToken);
-
-            if (string.IsNullOrEmpty(path))
-            {
-                return string.Empty;
-            }
-
             var seconds = TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds;
 
-            return string.Format(",ass='{0}',setpts=PTS -{1}/TB",
-                path.Replace('\\', '/').Replace(":/", "\\:/"),
-                Math.Round(seconds).ToString(UsCulture));
-        }
-
-        /// <summary>
-        /// Gets the extracted ass path.
-        /// </summary>
-        /// <param name="state">The state.</param>
-        /// <param name="performConversion">if set to <c>true</c> [perform conversion].</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>System.String.</returns>
-        private string GetExtractedAssPath(StreamState state,
-            bool performConversion,
-            CancellationToken cancellationToken)
-        {
-            var path = EncodingManager.GetSubtitleCachePath(state.MediaPath, state.SubtitleStream.Index, ".ass");
-
-            if (performConversion)
+            if (state.SubtitleStream.IsExternal)
             {
-                InputType type;
+                var subtitlePath = state.SubtitleStream.Path;
 
-                var inputPath = MediaEncoderHelpers.GetInputArgument(state.MediaPath, state.IsRemote, state.VideoType, state.IsoType, null, state.PlayableStreamFileNames, out type);
+                var charsetParam = string.Empty;
 
-                try
+                if (!string.IsNullOrEmpty(state.SubtitleStream.Language))
                 {
-                    var parentPath = Path.GetDirectoryName(path);
+                    var charenc = MediaEncoder.GetSubtitleLanguageEncodingParam(subtitlePath, state.SubtitleStream.Language);
 
-                    Directory.CreateDirectory(parentPath);
-
-                    // Don't re-encode ass/ssa to ass because ffmpeg ass encoder fails if there's more than one ass rectangle. Affect Anime mostly.
-                    // See https://lists.ffmpeg.org/pipermail/ffmpeg-cvslog/2013-April/063616.html
-                    var isAssSubtitle = string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase);
-
-                    var task = MediaEncoder.ExtractTextSubtitle(inputPath, type, state.SubtitleStream.Index, isAssSubtitle, path, cancellationToken);
-
-                    Task.WaitAll(task);
-                }
-                catch
-                {
-                    return null;
+                    if (!string.IsNullOrEmpty(charenc))
+                    {
+                        charsetParam = ":charenc=" + charenc;
+                    }
                 }
-            }
 
-            return path;
-        }
-
-        /// <summary>
-        /// Gets the converted ass path.
-        /// </summary>
-        /// <param name="subtitleStream">The subtitle stream.</param>
-        /// <param name="performConversion">if set to <c>true</c> [perform conversion].</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>System.String.</returns>
-        private string GetConvertedAssPath(MediaStream subtitleStream,
-            bool performConversion,
-            CancellationToken cancellationToken)
-        {
-            var path = EncodingManager.GetSubtitleCachePath(subtitleStream.Path, ".ass");
+                // TODO: Perhaps also use original_size=1920x800
 
-            if (performConversion)
-            {
-                try
-                {
-                    var parentPath = Path.GetDirectoryName(path);
-
-                    Directory.CreateDirectory(parentPath);
-
-                    var task = MediaEncoder.ConvertTextSubtitleToAss(subtitleStream.Path, path, subtitleStream.Language, cancellationToken);
-
-                    Task.WaitAll(task);
-                }
-                catch
-                {
-                    return null;
-                }
+                return string.Format(",subtitles=filename='{0}'{1},setpts=PTS -{2}/TB",
+                    subtitlePath.Replace('\\', '/').Replace(":/", "\\:/"),
+                    charsetParam,
+                    Math.Round(seconds).ToString(UsCulture));
             }
 
-            return path;
+            return string.Format(",subtitles='{0}:si={1}',setpts=PTS -{2}/TB",
+                state.MediaPath.Replace('\\', '/').Replace(":/", "\\:/"),
+                state.SubtitleStream.Index.ToString(UsCulture),
+                Math.Round(seconds).ToString(UsCulture));
         }
 
         /// <summary>
@@ -688,7 +624,7 @@ namespace MediaBrowser.Api.Playback
             // Add resolution params, if specified
             if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue)
             {
-                outputSizeParam = GetOutputSizeParam(state, outputVideoCodec, false, CancellationToken.None).TrimEnd('"');
+                outputSizeParam = GetOutputSizeParam(state, outputVideoCodec, CancellationToken.None).TrimEnd('"');
                 outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase));
             }
 
@@ -1585,7 +1521,7 @@ namespace MediaBrowser.Api.Playback
             state.OutputAudioCodec = GetAudioCodec(state.Request);
 
             state.OutputAudioChannels = GetNumAudioChannelsParam(state.Request, state.AudioStream, state.OutputAudioCodec);
-            
+
             if (videoRequest != null)
             {
                 state.OutputVideoCodec = GetVideoCodec(videoRequest);
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index d76fbc439e..7258a01741 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -39,9 +39,8 @@ namespace MediaBrowser.Api.Playback.Hls
         /// Gets the video arguments.
         /// </summary>
         /// <param name="state">The state.</param>
-        /// <param name="performSubtitleConversion">if set to <c>true</c> [perform subtitle conversion].</param>
         /// <returns>System.String.</returns>
-        protected abstract string GetVideoArguments(StreamState state, bool performSubtitleConversion);
+        protected abstract string GetVideoArguments(StreamState state);
 
         /// <summary>
         /// Gets the segment file extension.
@@ -272,9 +271,9 @@ namespace MediaBrowser.Api.Playback.Hls
         /// </summary>
         /// <param name="outputPath">The output path.</param>
         /// <param name="state">The state.</param>
-        /// <param name="performSubtitleConversions">if set to <c>true</c> [perform subtitle conversions].</param>
+        /// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
         /// <returns>System.String.</returns>
-        protected override string GetCommandLineArguments(string outputPath, StreamState state, bool performSubtitleConversions)
+        protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
         {
             var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream;
 
@@ -289,7 +288,7 @@ namespace MediaBrowser.Api.Playback.Hls
             var inputModifier = GetInputModifier(state);
 
             // If performSubtitleConversions is true we're actually starting ffmpeg
-            var startNumberParam = performSubtitleConversions ? GetStartNumber(state).ToString(UsCulture) : "0";
+            var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0";
 
             var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"",
                 itsOffset,
@@ -297,7 +296,7 @@ namespace MediaBrowser.Api.Playback.Hls
                 GetInputArgument(state),
                 threads,
                 GetMapArgs(state),
-                GetVideoArguments(state, performSubtitleConversions),
+                GetVideoArguments(state),
                 GetAudioArguments(state),
                 state.SegmentLength.ToString(UsCulture),
                 startNumberParam,
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 82ac70334b..92d6161f5a 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -264,7 +264,7 @@ namespace MediaBrowser.Api.Playback.Hls
             return args;
         }
 
-        protected override string GetVideoArguments(StreamState state, bool performSubtitleConversion)
+        protected override string GetVideoArguments(StreamState state)
         {
             var codec = state.OutputVideoCodec;
 
@@ -285,7 +285,7 @@ namespace MediaBrowser.Api.Playback.Hls
             {
                 if (state.VideoRequest.Width.HasValue || state.VideoRequest.Height.HasValue || state.VideoRequest.MaxHeight.HasValue || state.VideoRequest.MaxWidth.HasValue)
                 {
-                    args += GetOutputSizeParam(state, codec, performSubtitleConversion, CancellationToken.None);
+                    args += GetOutputSizeParam(state, codec, CancellationToken.None);
                 }
             }
 
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
index b7eacb0c33..346b379609 100644
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
@@ -155,10 +155,8 @@ namespace MediaBrowser.Api.Playback.Hls
         /// Gets the video arguments.
         /// </summary>
         /// <param name="state">The state.</param>
-        /// <param name="performSubtitleConversion">if set to <c>true</c> [perform subtitle conversion].</param>
         /// <returns>System.String.</returns>
-        protected override string GetVideoArguments(StreamState state, 
-            bool performSubtitleConversion)
+        protected override string GetVideoArguments(StreamState state)
         {
             var codec = state.OutputVideoCodec;
 
@@ -181,7 +179,7 @@ namespace MediaBrowser.Api.Playback.Hls
             {
                 if (state.VideoRequest.Width.HasValue || state.VideoRequest.Height.HasValue || state.VideoRequest.MaxHeight.HasValue || state.VideoRequest.MaxWidth.HasValue)
                 {
-                    args += GetOutputSizeParam(state, codec, performSubtitleConversion, CancellationToken.None);
+                    args += GetOutputSizeParam(state, codec, CancellationToken.None);
                 }
             }
 
diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
index 78f12b0970..44065dfe89 100644
--- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
@@ -73,10 +73,10 @@ namespace MediaBrowser.Api.Playback.Progressive
         /// </summary>
         /// <param name="outputPath">The output path.</param>
         /// <param name="state">The state.</param>
-        /// <param name="performSubtitleConversions">if set to <c>true</c> [perform subtitle conversions].</param>
+        /// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
         /// <returns>System.String.</returns>
         /// <exception cref="System.InvalidOperationException">Only aac and mp3 audio codecs are supported.</exception>
-        protected override string GetCommandLineArguments(string outputPath, StreamState state, bool performSubtitleConversions)
+        protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
         {
             var audioTranscodeParams = new List<string>();
 
diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
index 0ce33fca80..4b9d0fbb5c 100644
--- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
@@ -1,4 +1,3 @@
-using System.Threading;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Channels;
@@ -14,6 +13,7 @@ using MediaBrowser.Model.IO;
 using ServiceStack;
 using System;
 using System.IO;
+using System.Threading;
 
 namespace MediaBrowser.Api.Playback.Progressive
 {
@@ -90,9 +90,9 @@ namespace MediaBrowser.Api.Playback.Progressive
         /// </summary>
         /// <param name="outputPath">The output path.</param>
         /// <param name="state">The state.</param>
-        /// <param name="performSubtitleConversions">if set to <c>true</c> [perform subtitle conversions].</param>
+        /// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
         /// <returns>System.String.</returns>
-        protected override string GetCommandLineArguments(string outputPath, StreamState state, bool performSubtitleConversions)
+        protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
         {
             // Get the output codec name
             var videoCodec = state.OutputVideoCodec;
@@ -114,7 +114,7 @@ namespace MediaBrowser.Api.Playback.Progressive
                 GetInputArgument(state),
                 keyFrame,
                 GetMapArgs(state),
-                GetVideoArguments(state, videoCodec, performSubtitleConversions),
+                GetVideoArguments(state, videoCodec),
                 threads,
                 GetAudioArguments(state),
                 format,
@@ -127,9 +127,8 @@ namespace MediaBrowser.Api.Playback.Progressive
         /// </summary>
         /// <param name="state">The state.</param>
         /// <param name="codec">The video codec.</param>
-        /// <param name="performSubtitleConversion">if set to <c>true</c> [perform subtitle conversion].</param>
         /// <returns>System.String.</returns>
-        private string GetVideoArguments(StreamState state, string codec, bool performSubtitleConversion)
+        private string GetVideoArguments(StreamState state, string codec)
         {
             var args = "-vcodec " + codec;
 
@@ -157,7 +156,7 @@ namespace MediaBrowser.Api.Playback.Progressive
             {
                 if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue)
                 {
-                    args += GetOutputSizeParam(state, codec, performSubtitleConversion, CancellationToken.None);
+                    args += GetOutputSizeParam(state, codec, CancellationToken.None);
                 }
             }
 
diff --git a/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs b/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs
index a291d6e27f..c2a51654c7 100644
--- a/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs
+++ b/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs
@@ -6,4 +6,9 @@
 
         public string UserId { get; set; }
     }
+
+    public class ChannelLatestMediaSearch
+    {
+        public string UserId { get; set; }
+    }
 }
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/ISearchableChannel.cs b/MediaBrowser.Controller/Channels/ISearchableChannel.cs
index c5fb3a4f19..d637084942 100644
--- a/MediaBrowser.Controller/Channels/ISearchableChannel.cs
+++ b/MediaBrowser.Controller/Channels/ISearchableChannel.cs
@@ -14,4 +14,15 @@ namespace MediaBrowser.Controller.Channels
         /// <returns>Task{IEnumerable{ChannelItemInfo}}.</returns>
         Task<IEnumerable<ChannelItemInfo>> Search(ChannelSearchInfo searchInfo, CancellationToken cancellationToken);
     }
+
+    public interface ISupportsLatestMedia
+    {
+        /// <summary>
+        /// Gets the latest media.
+        /// </summary>
+        /// <param name="request">The request.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task{IEnumerable{ChannelItemInfo}}.</returns>
+        Task<IEnumerable<ChannelItemInfo>> GetLatestMedia(ChannelLatestMediaSearch request, CancellationToken cancellationToken);
+    }
 }
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Chapters/IChapterManager.cs b/MediaBrowser.Controller/Chapters/IChapterManager.cs
index df230bf7e3..b8f29d1ceb 100644
--- a/MediaBrowser.Controller/Chapters/IChapterManager.cs
+++ b/MediaBrowser.Controller/Chapters/IChapterManager.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Model.Chapters;
 using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Controller.Chapters
 {
@@ -17,6 +18,22 @@ namespace MediaBrowser.Controller.Chapters
         /// <param name="chapterProviders">The chapter providers.</param>
         void AddParts(IEnumerable<IChapterProvider> chapterProviders);
 
+        /// <summary>
+        /// Gets the chapters.
+        /// </summary>
+        /// <param name="itemId">The item identifier.</param>
+        /// <returns>List{ChapterInfo}.</returns>
+        IEnumerable<ChapterInfo> GetChapters(string itemId);
+
+        /// <summary>
+        /// Saves the chapters.
+        /// </summary>
+        /// <param name="itemId">The item identifier.</param>
+        /// <param name="chapters">The chapters.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        Task SaveChapters(string itemId, IEnumerable<ChapterInfo> chapters, CancellationToken cancellationToken);
+
         /// <summary>
         /// Searches the specified video.
         /// </summary>
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
index 8e66a1be91..d40ecb463c 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
@@ -274,5 +274,11 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>BaseItemDto.</returns>
         Task<BaseItemDto> GetLiveTvFolder(string userId, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Gets the enabled users.
+        /// </summary>
+        /// <returns>IEnumerable{User}.</returns>
+        IEnumerable<User> GetEnabledUsers();
     }
 }
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 05e8ba2fb2..83155fe8cd 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -192,6 +192,7 @@
     <Compile Include="MediaEncoding\ImageEncodingOptions.cs" />
     <Compile Include="MediaEncoding\IMediaEncoder.cs" />
     <Compile Include="MediaEncoding\InternalMediaInfoResult.cs" />
+    <Compile Include="MediaEncoding\ISubtitleEncoder.cs" />
     <Compile Include="MediaEncoding\VideoEncodingOptions.cs" />
     <Compile Include="Net\IHasResultFactory.cs" />
     <Compile Include="Net\IHttpResultFactory.cs" />
diff --git a/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs b/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs
index d1e40e3f06..8683a6af42 100644
--- a/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs
@@ -5,23 +5,6 @@ namespace MediaBrowser.Controller.MediaEncoding
 {
     public interface IEncodingManager
     {
-        /// <summary>
-        /// Gets the subtitle cache path.
-        /// </summary>
-        /// <param name="originalSubtitlePath">The original subtitle path.</param>
-        /// <param name="outputSubtitleExtension">The output subtitle extension.</param>
-        /// <returns>System.String.</returns>
-        string GetSubtitleCachePath(string originalSubtitlePath, string outputSubtitleExtension);
-
-        /// <summary>
-        /// Gets the subtitle cache path.
-        /// </summary>
-        /// <param name="mediaPath">The media path.</param>
-        /// <param name="subtitleStreamIndex">Index of the subtitle stream.</param>
-        /// <param name="outputSubtitleExtension">The output subtitle extension.</param>
-        /// <returns>System.String.</returns>
-        string GetSubtitleCachePath(string mediaPath, int subtitleStreamIndex, string outputSubtitleExtension);
-
         /// <summary>
         /// Refreshes the chapter images.
         /// </summary>
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index e9081fe8a3..9895d9a60b 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -55,14 +55,12 @@ namespace MediaBrowser.Controller.MediaEncoding
         Task ExtractTextSubtitle(string[] inputFiles, InputType type, int subtitleStreamIndex, bool copySubtitleStream, string outputPath, CancellationToken cancellationToken);
 
         /// <summary>
-        /// Converts the text subtitle to ass.
+        /// Gets the subtitle language encoding parameter.
         /// </summary>
-        /// <param name="inputPath">The input path.</param>
-        /// <param name="outputPath">The output path.</param>
+        /// <param name="path">The path.</param>
         /// <param name="language">The language.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language, CancellationToken cancellationToken);
+        /// <returns>System.String.</returns>
+        string GetSubtitleLanguageEncodingParam(string path, string language);
 
         /// <summary>
         /// Gets the media info.
@@ -88,14 +86,6 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// <param name="type">The type.</param>
         /// <returns>System.String.</returns>
         string GetInputArgument(string[] inputFiles, InputType type);
-
-        /// <summary>
-        /// Encodes the image.
-        /// </summary>
-        /// <param name="options">The options.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task{Stream}.</returns>
-        Task<Stream> EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken);
     }
 
     /// <summary>
diff --git a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
new file mode 100644
index 0000000000..f171d6f77c
--- /dev/null
+++ b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
@@ -0,0 +1,15 @@
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.MediaEncoding
+{
+    public interface ISubtitleEncoder
+    {
+        Task<Stream> ConvertTextSubtitle(String stream, 
+            string inputFormat, 
+            string outputFormat,
+            CancellationToken cancellationToken);
+    }
+}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 9ff28dda21..c14e7d476d 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -261,168 +261,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
             ((Process)sender).Dispose();
         }
 
-        /// <summary>
-        /// Converts the text subtitle to ass.
-        /// </summary>
-        /// <param name="inputPath">The input path.</param>
-        /// <param name="outputPath">The output path.</param>
-        /// <param name="language">The language.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        public async Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language,
-            CancellationToken cancellationToken)
-        {
-            var semaphore = GetLock(outputPath);
-
-            await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
-            try
-            {
-                if (!File.Exists(outputPath))
-                {
-                    await ConvertTextSubtitleToAssInternal(inputPath, outputPath, language).ConfigureAwait(false);
-                }
-            }
-            finally
-            {
-                semaphore.Release();
-            }
-        }
-
         private const int FastSeekOffsetSeconds = 1;
 
-        /// <summary>
-        /// Converts the text subtitle to ass.
-        /// </summary>
-        /// <param name="inputPath">The input path.</param>
-        /// <param name="outputPath">The output path.</param>
-        /// <param name="language">The language.</param>
-        /// <returns>Task.</returns>
-        /// <exception cref="System.ArgumentNullException">inputPath
-        /// or
-        /// outputPath</exception>
-        /// <exception cref="System.ApplicationException"></exception>
-        private async Task ConvertTextSubtitleToAssInternal(string inputPath, string outputPath, string language)
-        {
-            if (string.IsNullOrEmpty(inputPath))
-            {
-                throw new ArgumentNullException("inputPath");
-            }
-
-            if (string.IsNullOrEmpty(outputPath))
-            {
-                throw new ArgumentNullException("outputPath");
-            }
-
-
-            var encodingParam = string.IsNullOrEmpty(language)
-                ? string.Empty
-                : GetSubtitleLanguageEncodingParam(language) + " ";
-
-            var process = new Process
-            {
-                StartInfo = new ProcessStartInfo
-                {
-                    RedirectStandardOutput = false,
-                    RedirectStandardError = true,
-
-                    CreateNoWindow = true,
-                    UseShellExecute = false,
-                    FileName = FFMpegPath,
-                    Arguments =
-                        string.Format("{0} -i \"{1}\" -c:s ass \"{2}\"", encodingParam, inputPath, outputPath),
-
-                    WindowStyle = ProcessWindowStyle.Hidden,
-                    ErrorDialog = false
-                }
-            };
-
-            _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
-
-            var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-sub-convert-" + 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;
-            }
-
-            var logTask = process.StandardError.BaseStream.CopyToAsync(logFileStream);
-
-            var ranToCompletion = process.WaitForExit(60000);
-
-            if (!ranToCompletion)
-            {
-                try
-                {
-                    _logger.Info("Killing ffmpeg subtitle conversion process");
-
-                    process.Kill();
-
-                    process.WaitForExit(1000);
-
-                    await logTask.ConfigureAwait(false);
-                }
-                catch (Exception ex)
-                {
-                    _logger.ErrorException("Error killing subtitle conversion 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 converted subtitle due to failure: ", outputPath);
-                        File.Delete(outputPath);
-                    }
-                    catch (IOException ex)
-                    {
-                        _logger.ErrorException("Error deleting converted subtitle {0}", ex, outputPath);
-                    }
-                }
-            }
-            else if (!File.Exists(outputPath))
-            {
-                failed = true;
-            }
-
-            if (failed)
-            {
-                var msg = string.Format("ffmpeg subtitle converted failed for {0}", inputPath);
-
-                _logger.Error(msg);
-
-                throw new ApplicationException(msg);
-            }
-            await SetAssFont(outputPath).ConfigureAwait(false);
-        }
-
         protected string GetFastSeekCommandLineParameter(TimeSpan offset)
         {
             var seconds = offset.TotalSeconds - FastSeekOffsetSeconds;
@@ -448,10 +288,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
         /// <summary>
         /// Gets the subtitle language encoding param.
         /// </summary>
+        /// <param name="path">The path.</param>
         /// <param name="language">The language.</param>
         /// <returns>System.String.</returns>
-        private string GetSubtitleLanguageEncodingParam(string language)
+        public string GetSubtitleLanguageEncodingParam(string path, string language)
         {
+            if (GetFileEncoding(path).Equals(Encoding.UTF8))
+            {
+                return string.Empty;
+            }
+
             switch (language.ToLower())
             {
                 case "pol":
@@ -468,29 +314,52 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 case "rup":
                 case "alb":
                 case "sqi":
-                    return "-sub_charenc windows-1250";
+                    return "windows-1250";
                 case "ara":
-                    return "-sub_charenc windows-1256";
+                    return "windows-1256";
                 case "heb":
-                    return "-sub_charenc windows-1255";
+                    return "windows-1255";
                 case "grc":
                 case "gre":
-                    return "-sub_charenc windows-1253";
+                    return "windows-1253";
                 case "crh":
                 case "ota":
                 case "tur":
-                    return "-sub_charenc windows-1254";
+                    return "windows-1254";
                 case "rus":
-                    return "-sub_charenc windows-1251";
+                    return "windows-1251";
                 case "vie":
-                    return "-sub_charenc windows-1258";
+                    return "windows-1258";
                 case "kor":
-                    return "-sub_charenc cp949";
+                    return "cp949";
                 default:
-                    return "-sub_charenc windows-1252";
+                    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>
diff --git a/MediaBrowser.Model/Channels/ChannelFeatures.cs b/MediaBrowser.Model/Channels/ChannelFeatures.cs
index 3333626883..8c41bfde75 100644
--- a/MediaBrowser.Model/Channels/ChannelFeatures.cs
+++ b/MediaBrowser.Model/Channels/ChannelFeatures.cs
@@ -50,6 +50,12 @@ namespace MediaBrowser.Model.Channels
         /// </summary>
         public bool SupportsSortOrderToggle { get; set; }
 
+        /// <summary>
+        /// Gets or sets a value indicating whether [supports latest media].
+        /// </summary>
+        /// <value><c>true</c> if [supports latest media]; otherwise, <c>false</c>.</value>
+        public bool SupportsLatestMedia { get; set; }
+        
         /// <summary>
         /// Gets or sets a value indicating whether this instance can filter.
         /// </summary>
diff --git a/MediaBrowser.Providers/Chapters/ChapterManager.cs b/MediaBrowser.Providers/Chapters/ChapterManager.cs
index 2723d80a2e..c020d0721f 100644
--- a/MediaBrowser.Providers/Chapters/ChapterManager.cs
+++ b/MediaBrowser.Providers/Chapters/ChapterManager.cs
@@ -5,8 +5,10 @@ using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Chapters;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using System;
 using System.Collections.Generic;
@@ -22,12 +24,14 @@ namespace MediaBrowser.Providers.Chapters
         private readonly ILibraryManager _libraryManager;
         private readonly ILogger _logger;
         private readonly IServerConfigurationManager _config;
+        private readonly IItemRepository _itemRepo;
 
-        public ChapterManager(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config)
+        public ChapterManager(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IItemRepository itemRepo)
         {
             _libraryManager = libraryManager;
             _logger = logger;
             _config = config;
+            _itemRepo = itemRepo;
         }
 
         public void AddParts(IEnumerable<IChapterProvider> chapterProviders)
@@ -236,5 +240,15 @@ namespace MediaBrowser.Providers.Chapters
 
             return 0;
         }
+
+        public IEnumerable<ChapterInfo> GetChapters(string itemId)
+        {
+            return _itemRepo.GetChapters(new Guid(itemId));
+        }
+
+        public Task SaveChapters(string itemId, IEnumerable<ChapterInfo> chapters, CancellationToken cancellationToken)
+        {
+            return _itemRepo.SaveChapters(new Guid(itemId), chapters, cancellationToken);
+        }
     }
 }
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index 61610a5f1a..8729f68ebe 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -261,7 +261,7 @@ namespace MediaBrowser.Providers.MediaInfo
 
                 }, cancellationToken).ConfigureAwait(false);
 
-                await _itemRepo.SaveChapters(video.Id, chapters, cancellationToken).ConfigureAwait(false);
+                await _chapterManager.SaveChapters(video.Id.ToString(), chapters, cancellationToken).ConfigureAwait(false);
             }
         }
 
@@ -279,8 +279,14 @@ namespace MediaBrowser.Providers.MediaInfo
             }
 
             // Limit accuracy to milliseconds to match xml saving
-            var ms = Math.Round(TimeSpan.FromTicks(chapter.start / 100).TotalMilliseconds);
-            info.StartPositionTicks = TimeSpan.FromMilliseconds(ms).Ticks;
+            var secondsString = chapter.start_time;
+            double seconds;
+
+            if (double.TryParse(secondsString, NumberStyles.Any, CultureInfo.InvariantCulture, out seconds))
+            {
+                var ms = Math.Round(TimeSpan.FromSeconds(seconds).TotalMilliseconds);
+                info.StartPositionTicks = TimeSpan.FromMilliseconds(ms).Ticks;
+            }
 
             return info;
         }
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
index 69a681ead2..553c683fd0 100644
--- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
+++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
@@ -453,6 +453,7 @@ namespace MediaBrowser.Server.Implementations.Channels
                 MaxPageSize = features.MaxPageSize,
                 MediaTypes = features.MediaTypes,
                 SupportsSortOrderToggle = features.SupportsSortOrderToggle,
+                SupportsLatestMedia = provider is ISupportsLatestMedia,
                 Name = channel.Name,
                 Id = channel.Id.ToString("N"),
                 CanDownloadAllMedia = isIndexable
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
index db0cf33c4a..e19a6c7d67 100644
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
@@ -133,8 +133,10 @@ namespace MediaBrowser.Server.Implementations.Dto
                         ? null
                         : source.MediaStreams.Where(i => i.Type == MediaStreamType.Audio && i.Index == defaultAudioIndex).Select(i => i.Language).FirstOrDefault();
 
-                    source.DefaultSubtitleStreamIndex = MediaStreamSelector.GetDefaultSubtitleStreamIndex(source.MediaStreams, preferredSubs,
-                            user.Configuration.SubtitleMode, audioLangage);
+                    source.DefaultSubtitleStreamIndex = MediaStreamSelector.GetDefaultSubtitleStreamIndex(source.MediaStreams, 
+                        preferredSubs,
+                        user.Configuration.SubtitleMode, 
+                        audioLangage);
                 }
             }
 
diff --git a/MediaBrowser.Server.Implementations/Dto/MediaStreamSelector.cs b/MediaBrowser.Server.Implementations/Dto/MediaStreamSelector.cs
index 10c78b300b..12e70598fe 100644
--- a/MediaBrowser.Server.Implementations/Dto/MediaStreamSelector.cs
+++ b/MediaBrowser.Server.Implementations/Dto/MediaStreamSelector.cs
@@ -38,7 +38,7 @@ namespace MediaBrowser.Server.Implementations.Dto
             SubtitlePlaybackMode mode,
             string audioTrackLanguage)
         {
-            var languages = preferredLanguages as List<string> ?? preferredLanguages.ToList();
+            var languages = preferredLanguages.ToList();
             streams = GetSortedStreams(streams, MediaStreamType.Subtitle, languages).ToList();
 
             var full = streams.Where(s => !s.IsForced);
@@ -87,7 +87,12 @@ namespace MediaBrowser.Server.Implementations.Dto
                 .Where(i => i.Type == type);
 
             // Give some preferance to external text subs for better performance
-            return orderStreams.OrderBy(i => languagePreferences.FindIndex(l => string.Equals(i.Language, l, StringComparison.OrdinalIgnoreCase)))
+            return orderStreams.OrderBy(i =>
+            {
+                var index = languagePreferences.FindIndex(l => string.Equals(i.Language, l, StringComparison.OrdinalIgnoreCase));
+
+                return index == -1 ? 100 : index;
+            })
                  .ThenBy(i => i.IsDefault)
                  .ThenBy(i => !i.IsGraphicalSubtitleStream)
                  .ThenBy(i => i.IsExternal)
diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
index b9a96bfc97..fc4b9eb4ca 100644
--- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
@@ -88,29 +88,19 @@ namespace MediaBrowser.Server.Implementations.Library
 
             if (query.IncludeExternalContent)
             {
-                var channelsTask = Task.Run(() => _channelManager.GetChannels(new ChannelQuery
+                var channelResult = await _channelManager.GetChannels(new ChannelQuery
                 {
                     Limit = 0,
                     UserId = query.UserId
 
-                }, cancellationToken), cancellationToken);
-
-                // Avoid implicitly captured closure.
-                var token = cancellationToken;
-                var liveTvTask = Task.Run(() => _liveTvManager.GetLiveTvInfo(token), cancellationToken);
-
-                await Task.WhenAll(channelsTask, liveTvTask).ConfigureAwait(false);
-
-                var channelResult = channelsTask.Result;
+                }, cancellationToken).ConfigureAwait(false);
 
                 if (channelResult.TotalRecordCount > 0)
                 {
                     list.Add(await _channelManager.GetInternalChannelFolder(query.UserId, cancellationToken).ConfigureAwait(false));
                 }
 
-                var liveTvInfo = liveTvTask.Result;
-
-                if (liveTvInfo.EnabledUsers.Contains(query.UserId))
+                if (_liveTvManager.GetEnabledUsers().Select(i => i.Id.ToString("N")).Contains(query.UserId))
                 {
                     list.Add(await _liveTvManager.GetInternalLiveTvFolder(query.UserId, cancellationToken).ConfigureAwait(false));
                 }
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index 625dd7e82d..a555e1f9c8 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -1734,6 +1734,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             return info;
         }
 
+        public IEnumerable<User> GetEnabledUsers()
+        {
+            var service = ActiveService;
+
+            return _userManager.Users
+                .Where(i => i.Configuration.EnableLiveTvAccess && service != null);
+        }
+
         /// <summary>
         /// Resets the tuner.
         /// </summary>
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json
index 74ac9aeeb9..2eec7432ce 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/server.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json
@@ -733,7 +733,7 @@
 	"OptionReportByteRangeSeekingWhenTranscodingHelp": "This is required for some devices that don't time seek very well.",
 	"HeaderSubtitleDownloadingHelp": "When Media Browser scans your video files it can search for missing subtitles, and download them using a subtitle provider such as OpenSubtitles.org.",
 	"HeaderDownloadSubtitlesFor": "Download subtitles for:",
-	"MessageNoChapterProviders": "Install a chapter provider plugin such as ChapterDb or tagChimp to enable additional chapter metadata options.",
+	"MessageNoChapterProviders": "Install a chapter provider plugin such as ChapterDb or tagChimp to enable additional chapter options.",
 	"LabelSkipIfGraphicalSubsPresent": "Skip if the video already contains graphical subtitles",
 	"LabelSkipIfGraphicalSubsPresentHelp": "Keeping text versions of subtitles will result in more efficient delivery to mobile clients.",
 	"TabSubtitles": "Subtitles",
diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
index ee3e154755..f4b8671698 100644
--- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
+++ b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
@@ -1,11 +1,10 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Chapters;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using System;
@@ -24,48 +23,16 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
         private readonly IFileSystem _fileSystem;
         private readonly ILogger _logger;
-        private readonly IItemRepository _itemRepo;
         private readonly IMediaEncoder _encoder;
+        private readonly IChapterManager _chapterManager;
 
-        public EncodingManager(IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IMediaEncoder encoder)
+        public EncodingManager(IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, IMediaEncoder encoder, IChapterManager chapterManager)
         {
             _config = config;
             _fileSystem = fileSystem;
             _logger = logger;
-            _itemRepo = itemRepo;
             _encoder = encoder;
-        }
-
-        private string SubtitleCachePath
-        {
-            get
-            {
-                return Path.Combine(_config.ApplicationPaths.CachePath, "subtitles");
-            }
-        }
-        
-        public string GetSubtitleCachePath(string originalSubtitlePath, string outputSubtitleExtension)
-        {
-            var ticksParam = _fileSystem.GetLastWriteTimeUtc(originalSubtitlePath).Ticks;
-
-            var filename = (originalSubtitlePath + ticksParam).GetMD5() + outputSubtitleExtension;
-
-            var prefix = filename.Substring(0, 1);
-
-            return Path.Combine(SubtitleCachePath, prefix, filename);
-        }
-
-        public string GetSubtitleCachePath(string mediaPath, int subtitleStreamIndex, string outputSubtitleExtension)
-        {
-            var ticksParam = string.Empty;
-
-            var date = _fileSystem.GetLastWriteTimeUtc(mediaPath);
-
-            var filename = (mediaPath + "_" + subtitleStreamIndex.ToString(_usCulture) + "_" + date.Ticks.ToString(_usCulture) + ticksParam).GetMD5() + outputSubtitleExtension;
-
-            var prefix = filename.Substring(0, 1);
-
-            return Path.Combine(SubtitleCachePath, prefix, filename);
+            _chapterManager = chapterManager;
         }
 
         /// <summary>
@@ -202,7 +169,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
 
             if (saveChapters && changesMade)
             {
-                await _itemRepo.SaveChapters(video.Id, chapters, cancellationToken).ConfigureAwait(false);
+                await _chapterManager.SaveChapters(video.Id.ToString(), chapters, cancellationToken).ConfigureAwait(false);
             }
 
             DeleteDeadImages(currentImages, chapters);
diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs
index c4d6bce4d4..724641b242 100644
--- a/MediaBrowser.ServerApplication/ApplicationHost.cs
+++ b/MediaBrowser.ServerApplication/ApplicationHost.cs
@@ -511,10 +511,6 @@ namespace MediaBrowser.ServerApplication
 
             progress.Report(15);
 
-            EncodingManager = new EncodingManager(ServerConfigurationManager, FileSystemManager, Logger, ItemRepository,
-                MediaEncoder);
-            RegisterSingleInstance(EncodingManager);
-
             ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, Logger, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager);
             RegisterSingleInstance(ChannelManager);
 
@@ -547,9 +543,13 @@ namespace MediaBrowser.ServerApplication
             SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, ItemRepository);
             RegisterSingleInstance(SubtitleManager);
 
-            ChapterManager = new ChapterManager(LibraryManager, LogManager.GetLogger("ChapterManager"), ServerConfigurationManager);
+            ChapterManager = new ChapterManager(LibraryManager, LogManager.GetLogger("ChapterManager"), ServerConfigurationManager, ItemRepository);
             RegisterSingleInstance(ChapterManager);
 
+            EncodingManager = new EncodingManager(ServerConfigurationManager, FileSystemManager, Logger,
+                MediaEncoder, ChapterManager);
+            RegisterSingleInstance(EncodingManager);
+
             var displayPreferencesTask = Task.Run(async () => await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false));
             var itemsTask = Task.Run(async () => await ConfigureItemRepositories().ConfigureAwait(false));
             var userdataTask = Task.Run(async () => await ConfigureUserDataRepositories().ConfigureAwait(false));