aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2013-05-17 14:05:49 -0400
committerLuke Pulverenti <luke.pulverenti@gmail.com>2013-05-17 14:05:49 -0400
commite2d6a5c05df874cb812cbc0b85e7deda22ce567a (patch)
tree21805c896f0028544de14916ec7b457680dea52c
parentda7af24fca3b2462b971dce595cfa5e548311cce (diff)
support static trailer streaming
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj3
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs19
-rw-r--r--MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs64
-rw-r--r--MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs75
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs2
-rw-r--r--MediaBrowser.Api/UserLibrary/ItemsService.cs2
-rw-r--r--MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs2
-rw-r--r--MediaBrowser.Controller/Providers/TV/SeriesXmlParser.cs11
-rw-r--r--MediaBrowser.Controller/Session/ISessionManager.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Session/SessionManager.cs37
10 files changed, 195 insertions, 22 deletions
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index 2819a649a..fb06e35ea 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -56,6 +56,8 @@
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
+ <Reference Include="System.Net.Http" />
+ <Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.XML" />
</ItemGroup>
<ItemGroup>
@@ -82,6 +84,7 @@
<Compile Include="Playback\Progressive\BaseProgressiveStreamingService.cs" />
<Compile Include="Playback\BaseStreamingService.cs" />
<Compile Include="Playback\Progressive\ProgressiveStreamWriter.cs" />
+ <Compile Include="Playback\StaticRemoteStreamWriter.cs" />
<Compile Include="Playback\StreamRequest.cs" />
<Compile Include="Playback\StreamState.cs" />
<Compile Include="Playback\Progressive\VideoService.cs" />
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 04b6a656d..19b339cd7 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -622,9 +622,26 @@ namespace MediaBrowser.Api.Playback
/// <returns>System.String.</returns>
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)
{
- return "-user-agent \"QuickTime/7.6.2\"";
+ return "QuickTime/7.6.2";
}
return string.Empty;
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
index 9bcce87c8..50a0c9acc 100644
--- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
+++ b/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.MediaInfo;
using MediaBrowser.Common.Net;
@@ -188,6 +191,11 @@ namespace MediaBrowser.Api.Playback.Progressive
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 outputPathExists = File.Exists(outputPath);
@@ -210,6 +218,60 @@ namespace MediaBrowser.Api.Playback.Progressive
}
/// <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>
/// Gets the album art response.
/// </summary>
/// <param name="state">The state.</param>
diff --git a/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs b/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs
new file mode 100644
index 000000000..89e13acb3
--- /dev/null
+++ b/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);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
index 22098a368..26b0aa192 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
@@ -135,7 +135,7 @@ namespace MediaBrowser.Api.UserLibrary
{
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();
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index c57778fd6..fbf41eb38 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -456,7 +456,7 @@ namespace MediaBrowser.Api.UserLibrary
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
diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
index 4c51d1299..82a91fa46 100644
--- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
@@ -97,7 +97,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
};
client = new HttpClient(handler);
- client.Timeout = TimeSpan.FromSeconds(30);
+ client.Timeout = TimeSpan.FromSeconds(15);
_httpClients.TryAdd(host, client);
}
diff --git a/MediaBrowser.Controller/Providers/TV/SeriesXmlParser.cs b/MediaBrowser.Controller/Providers/TV/SeriesXmlParser.cs
index f63a47627..c03e2a7f5 100644
--- a/MediaBrowser.Controller/Providers/TV/SeriesXmlParser.cs
+++ b/MediaBrowser.Controller/Providers/TV/SeriesXmlParser.cs
@@ -63,8 +63,15 @@ namespace MediaBrowser.Controller.Providers.TV
}
case "Airs_Time":
- item.AirTime = reader.ReadElementContentAsString();
- break;
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.AirTime = val;
+ }
+ break;
+ }
case "SeriesName":
item.Name = reader.ReadElementContentAsString();
diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs
index 8227170d4..f28721f5f 100644
--- a/MediaBrowser.Controller/Session/ISessionManager.cs
+++ b/MediaBrowser.Controller/Session/ISessionManager.cs
@@ -52,7 +52,7 @@ namespace MediaBrowser.Controller.Session
/// <param name="deviceId">The device id.</param>
/// <param name="deviceName">Name of the device.</param>
/// <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>
/// Used to report playback progress for an item
diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
index 2f9c7e389..d3dbbc62b 100644
--- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs
+++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
@@ -200,7 +200,7 @@ namespace MediaBrowser.Server.Implementations.Session
/// <param name="deviceName">Name of the device.</param>
/// <exception cref="System.ArgumentNullException">
/// </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)
{
@@ -213,6 +213,15 @@ namespace MediaBrowser.Server.Implementations.Session
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
// Fire events to inform plugins
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);
- UpdatePlayState(item, data, positionTicks.Value, false);
+ UpdatePlayState(item, data, positionTicks.Value);
await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false);
}
@@ -297,7 +306,7 @@ namespace MediaBrowser.Server.Implementations.Session
if (positionTicks.HasValue)
{
- UpdatePlayState(item, data, positionTicks.Value, true);
+ UpdatePlayState(item, data, positionTicks.Value);
}
else
{
@@ -322,11 +331,12 @@ namespace MediaBrowser.Server.Implementations.Session
/// <param name="item">The item</param>
/// <param name="data">User data for the item</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 (positionTicks > 0 && item.RunTimeTicks.HasValue && item.RunTimeTicks > 0)
+ if (positionTicks > 0 && hasRuntime)
{
var pctIn = Decimal.Divide(positionTicks, item.RunTimeTicks.Value) * 100;
@@ -334,7 +344,6 @@ namespace MediaBrowser.Server.Implementations.Session
if (pctIn < _configurationManager.Configuration.MinResumePct)
{
positionTicks = 0;
- incrementPlayCount = false;
}
// 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)
{
- data.PlaybackPositionTicks = 0;
+ positionTicks = 0;
}
data.PlaybackPositionTicks = positionTicks;
-
- if (incrementPlayCount)
- {
- data.PlayCount++;
- data.LastPlayedDate = DateTime.UtcNow;
- }
}
}
}