Pārlūkot izejas kodu

support static trailer streaming

Luke Pulverenti 12 gadi atpakaļ
vecāks
revīzija
e2d6a5c05d

+ 3 - 0
MediaBrowser.Api/MediaBrowser.Api.csproj

@@ -56,6 +56,8 @@
     <Reference Include="Microsoft.CSharp" />
     <Reference Include="Microsoft.CSharp" />
     <Reference Include="System.Data" />
     <Reference Include="System.Data" />
     <Reference Include="System.Drawing" />
     <Reference Include="System.Drawing" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Net.Http.WebRequest" />
     <Reference Include="System.XML" />
     <Reference Include="System.XML" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
@@ -82,6 +84,7 @@
     <Compile Include="Playback\Progressive\BaseProgressiveStreamingService.cs" />
     <Compile Include="Playback\Progressive\BaseProgressiveStreamingService.cs" />
     <Compile Include="Playback\BaseStreamingService.cs" />
     <Compile Include="Playback\BaseStreamingService.cs" />
     <Compile Include="Playback\Progressive\ProgressiveStreamWriter.cs" />
     <Compile Include="Playback\Progressive\ProgressiveStreamWriter.cs" />
+    <Compile Include="Playback\StaticRemoteStreamWriter.cs" />
     <Compile Include="Playback\StreamRequest.cs" />
     <Compile Include="Playback\StreamRequest.cs" />
     <Compile Include="Playback\StreamState.cs" />
     <Compile Include="Playback\StreamState.cs" />
     <Compile Include="Playback\Progressive\VideoService.cs" />
     <Compile Include="Playback\Progressive\VideoService.cs" />

+ 18 - 1
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -621,10 +621,27 @@ namespace MediaBrowser.Api.Playback
         /// <param name="item">The item.</param>
         /// <param name="item">The item.</param>
         /// <returns>System.String.</returns>
         /// <returns>System.String.</returns>
         protected string GetUserAgentParam(BaseItem item)
         protected string GetUserAgentParam(BaseItem item)
+        {
+            var useragent = GetUserAgent(item);
+
+            if (!string.IsNullOrEmpty(useragent))
+            {
+                return "-user-agent \"" + useragent + "\"";
+            }
+
+            return string.Empty;
+        }
+
+        /// <summary>
+        /// Gets the user agent.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <returns>System.String.</returns>
+        protected string GetUserAgent(BaseItem item)
         {
         {
             if (item.Path.IndexOf("apple.com", StringComparison.OrdinalIgnoreCase) != -1)
             if (item.Path.IndexOf("apple.com", StringComparison.OrdinalIgnoreCase) != -1)
             {
             {
-                return "-user-agent \"QuickTime/7.6.2\"";
+                return "QuickTime/7.6.2";
             }
             }
 
 
             return string.Empty;
             return string.Empty;

+ 63 - 1
MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs

@@ -1,4 +1,7 @@
-using MediaBrowser.Api.Images;
+using System.Net;
+using System.Net.Cache;
+using System.Net.Http;
+using MediaBrowser.Api.Images;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
@@ -188,6 +191,11 @@ namespace MediaBrowser.Api.Playback.Progressive
 
 
             var responseHeaders = new Dictionary<string, string>();
             var responseHeaders = new Dictionary<string, string>();
 
 
+            if (request.Static && state.Item.LocationType == LocationType.Remote)
+            {
+                return GetStaticRemoteStreamResult(state.Item, responseHeaders, isHeadRequest).Result;
+            }
+
             var outputPath = GetOutputFilePath(state);
             var outputPath = GetOutputFilePath(state);
             var outputPathExists = File.Exists(outputPath);
             var outputPathExists = File.Exists(outputPath);
 
 
@@ -209,6 +217,60 @@ namespace MediaBrowser.Api.Playback.Progressive
             return GetStreamResult(state, responseHeaders, isHeadRequest).Result;
             return GetStreamResult(state, responseHeaders, isHeadRequest).Result;
         }
         }
 
 
+        /// <summary>
+        /// Gets the static remote stream result.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <param name="responseHeaders">The response headers.</param>
+        /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
+        /// <returns>Task{System.Object}.</returns>
+        private async Task<object> GetStaticRemoteStreamResult(BaseItem item, Dictionary<string, string> responseHeaders, bool isHeadRequest)
+        {
+            responseHeaders["Accept-Ranges"] = "none";
+
+            var httpClient = new HttpClient(new WebRequestHandler
+            {
+                CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache),
+                AutomaticDecompression = DecompressionMethods.None
+            });
+
+            using (var message = new HttpRequestMessage(HttpMethod.Get, item.Path))
+            {
+                var useragent = GetUserAgent(item);
+
+                if (!string.IsNullOrEmpty(useragent))
+                {
+                    message.Headers.Add("User-Agent", useragent);
+                }
+
+                var response = await httpClient.SendAsync(message, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
+
+                response.EnsureSuccessStatusCode();
+
+                var contentType = response.Content.Headers.ContentType.MediaType;
+
+                // Headers only
+                if (isHeadRequest)
+                {
+                    response.Dispose();
+
+                    return ResultFactory.GetResult(null, contentType, responseHeaders);
+                }
+
+                var result = new StaticRemoteStreamWriter(response, httpClient);
+
+                result.Options["Content-Type"] = contentType;
+
+                // Add the response headers to the result object
+                foreach (var header in responseHeaders)
+                {
+                    result.Options[header.Key] = header.Value;
+                }
+
+                return result;
+            }
+        }
+
         /// <summary>
         /// <summary>
         /// Gets the album art response.
         /// Gets the album art response.
         /// </summary>
         /// </summary>

+ 75 - 0
MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs

@@ -0,0 +1,75 @@
+using ServiceStack.Service;
+using ServiceStack.ServiceHost;
+using System.Collections.Generic;
+using System.IO;
+using System.Net.Http;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Api.Playback
+{
+    /// <summary>
+    /// Class StaticRemoteStreamWriter
+    /// </summary>
+    public class StaticRemoteStreamWriter : IStreamWriter, IHasOptions
+    {
+        /// <summary>
+        /// The _input stream
+        /// </summary>
+        private readonly HttpResponseMessage _msg;
+
+        private readonly HttpClient _client;
+
+        /// <summary>
+        /// The _options
+        /// </summary>
+        private readonly IDictionary<string, string> _options = new Dictionary<string, string>();
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="StaticRemoteStreamWriter"/> class.
+        /// </summary>
+        public StaticRemoteStreamWriter(HttpResponseMessage msg, HttpClient client)
+        {
+            _msg = msg;
+            _client = client;
+        }
+
+        /// <summary>
+        /// Gets the options.
+        /// </summary>
+        /// <value>The options.</value>
+        public IDictionary<string, string> Options
+        {
+            get { return _options; }
+        }
+
+        /// <summary>
+        /// Writes to.
+        /// </summary>
+        /// <param name="responseStream">The response stream.</param>
+        public void WriteTo(Stream responseStream)
+        {
+            var task = WriteToAsync(responseStream);
+
+            Task.WaitAll(task);
+        }
+
+        /// <summary>
+        /// Writes to async.
+        /// </summary>
+        /// <param name="responseStream">The response stream.</param>
+        /// <returns>Task.</returns>
+        public async Task WriteToAsync(Stream responseStream)
+        {
+            using (_client)
+            {
+                using (_msg)
+                {
+                    using (var input = await _msg.Content.ReadAsStreamAsync().ConfigureAwait(false))
+                    {
+                        await input.CopyToAsync(responseStream).ConfigureAwait(false);
+                    }
+                }
+            }
+        }
+    }
+}

+ 1 - 1
MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs

@@ -135,7 +135,7 @@ namespace MediaBrowser.Api.UserLibrary
         {
         {
             if (!string.IsNullOrEmpty(request.NameStartsWithOrGreater))
             if (!string.IsNullOrEmpty(request.NameStartsWithOrGreater))
             {
             {
-                items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.Name, StringComparison.OrdinalIgnoreCase) < 1);
+                items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.Name, StringComparison.CurrentCultureIgnoreCase) < 1);
             }
             }
 
 
             var filters = request.GetFilters().ToList();
             var filters = request.GetFilters().ToList();

+ 1 - 1
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -456,7 +456,7 @@ namespace MediaBrowser.Api.UserLibrary
 
 
             if (!string.IsNullOrEmpty(request.NameStartsWithOrGreater))
             if (!string.IsNullOrEmpty(request.NameStartsWithOrGreater))
             {
             {
-                items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.OrdinalIgnoreCase) < 1);
+                items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1);
             }
             }
 
 
             // Filter by Series Status
             // Filter by Series Status

+ 1 - 1
MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs

@@ -97,7 +97,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
                 };
                 };
 
 
                 client = new HttpClient(handler);
                 client = new HttpClient(handler);
-                client.Timeout = TimeSpan.FromSeconds(30);
+                client.Timeout = TimeSpan.FromSeconds(15);
                 _httpClients.TryAdd(host, client);
                 _httpClients.TryAdd(host, client);
             }
             }
 
 

+ 9 - 2
MediaBrowser.Controller/Providers/TV/SeriesXmlParser.cs

@@ -63,8 +63,15 @@ namespace MediaBrowser.Controller.Providers.TV
                     }
                     }
 
 
                 case "Airs_Time":
                 case "Airs_Time":
-                    item.AirTime = reader.ReadElementContentAsString();
-                    break;
+                    {
+                        var val = reader.ReadElementContentAsString();
+
+                        if (!string.IsNullOrWhiteSpace(val))
+                        {
+                            item.AirTime = val;
+                        }
+                        break;
+                    }
 
 
                 case "SeriesName":
                 case "SeriesName":
                     item.Name = reader.ReadElementContentAsString();
                     item.Name = reader.ReadElementContentAsString();

+ 1 - 1
MediaBrowser.Controller/Session/ISessionManager.cs

@@ -52,7 +52,7 @@ namespace MediaBrowser.Controller.Session
         /// <param name="deviceId">The device id.</param>
         /// <param name="deviceId">The device id.</param>
         /// <param name="deviceName">Name of the device.</param>
         /// <param name="deviceName">Name of the device.</param>
         /// <exception cref="System.ArgumentNullException"></exception>
         /// <exception cref="System.ArgumentNullException"></exception>
-        void OnPlaybackStart(User user, BaseItem item, string clientType, string deviceId, string deviceName);
+        Task OnPlaybackStart(User user, BaseItem item, string clientType, string deviceId, string deviceName);
 
 
         /// <summary>
         /// <summary>
         /// Used to report playback progress for an item
         /// Used to report playback progress for an item

+ 23 - 14
MediaBrowser.Server.Implementations/Session/SessionManager.cs

@@ -200,7 +200,7 @@ namespace MediaBrowser.Server.Implementations.Session
         /// <param name="deviceName">Name of the device.</param>
         /// <param name="deviceName">Name of the device.</param>
         /// <exception cref="System.ArgumentNullException">
         /// <exception cref="System.ArgumentNullException">
         /// </exception>
         /// </exception>
-        public void OnPlaybackStart(User user, BaseItem item, string clientType, string deviceId, string deviceName)
+        public async Task OnPlaybackStart(User user, BaseItem item, string clientType, string deviceId, string deviceName)
         {
         {
             if (user == null)
             if (user == null)
             {
             {
@@ -213,6 +213,15 @@ namespace MediaBrowser.Server.Implementations.Session
 
 
             UpdateNowPlayingItemId(user, clientType, deviceId, deviceName, item, false);
             UpdateNowPlayingItemId(user, clientType, deviceId, deviceName, item, false);
 
 
+            var key = item.GetUserDataKey();
+
+            var data = await _userDataRepository.GetUserData(user.Id, key).ConfigureAwait(false);
+
+            data.PlayCount++;
+            data.LastPlayedDate = DateTime.UtcNow;
+
+            await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false);
+            
             // Nothing to save here
             // Nothing to save here
             // Fire events to inform plugins
             // Fire events to inform plugins
             EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs
             EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs
@@ -254,7 +263,7 @@ namespace MediaBrowser.Server.Implementations.Session
             {
             {
                 var data = await _userDataRepository.GetUserData(user.Id, key).ConfigureAwait(false);
                 var data = await _userDataRepository.GetUserData(user.Id, key).ConfigureAwait(false);
 
 
-                UpdatePlayState(item, data, positionTicks.Value, false);
+                UpdatePlayState(item, data, positionTicks.Value);
                 await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false);
                 await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false);
             }
             }
 
 
@@ -297,7 +306,7 @@ namespace MediaBrowser.Server.Implementations.Session
 
 
             if (positionTicks.HasValue)
             if (positionTicks.HasValue)
             {
             {
-                UpdatePlayState(item, data, positionTicks.Value, true);
+                UpdatePlayState(item, data, positionTicks.Value);
             }
             }
             else
             else
             {
             {
@@ -322,11 +331,12 @@ namespace MediaBrowser.Server.Implementations.Session
         /// <param name="item">The item</param>
         /// <param name="item">The item</param>
         /// <param name="data">User data for the item</param>
         /// <param name="data">User data for the item</param>
         /// <param name="positionTicks">The current playback position</param>
         /// <param name="positionTicks">The current playback position</param>
-        /// <param name="incrementPlayCount">Whether or not to increment playcount</param>
-        private void UpdatePlayState(BaseItem item, UserItemData data, long positionTicks, bool incrementPlayCount)
+        private void UpdatePlayState(BaseItem item, UserItemData data, long positionTicks)
         {
         {
+            var hasRuntime = item.RunTimeTicks.HasValue && item.RunTimeTicks > 0;
+
             // If a position has been reported, and if we know the duration
             // If a position has been reported, and if we know the duration
-            if (positionTicks > 0 && item.RunTimeTicks.HasValue && item.RunTimeTicks > 0)
+            if (positionTicks > 0 && hasRuntime)
             {
             {
                 var pctIn = Decimal.Divide(positionTicks, item.RunTimeTicks.Value) * 100;
                 var pctIn = Decimal.Divide(positionTicks, item.RunTimeTicks.Value) * 100;
 
 
@@ -334,7 +344,6 @@ namespace MediaBrowser.Server.Implementations.Session
                 if (pctIn < _configurationManager.Configuration.MinResumePct)
                 if (pctIn < _configurationManager.Configuration.MinResumePct)
                 {
                 {
                     positionTicks = 0;
                     positionTicks = 0;
-                    incrementPlayCount = false;
                 }
                 }
 
 
                 // If we're at the end, assume completed
                 // If we're at the end, assume completed
@@ -356,19 +365,19 @@ namespace MediaBrowser.Server.Implementations.Session
                     }
                     }
                 }
                 }
             }
             }
+            else if (!hasRuntime)
+            {
+                // If we don't know the runtime we'll just have to assume it was fully played
+                data.Played = true;
+                positionTicks = 0;
+            }
 
 
             if (item is Audio)
             if (item is Audio)
             {
             {
-                data.PlaybackPositionTicks = 0;
+                positionTicks = 0;
             }
             }
 
 
             data.PlaybackPositionTicks = positionTicks;
             data.PlaybackPositionTicks = positionTicks;
-
-            if (incrementPlayCount)
-            {
-                data.PlayCount++;
-                data.LastPlayedDate = DateTime.UtcNow;
-            }
         }
         }
     }
     }
 }
 }