support static trailer streaming
This commit is contained in:
parent
da7af24fca
commit
e2d6a5c05d
|
@ -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" />
|
||||
|
|
|
@ -621,10 +621,27 @@ namespace MediaBrowser.Api.Playback
|
|||
/// <param name="item">The item.</param>
|
||||
/// <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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
@ -209,6 +217,60 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
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>
|
||||
/// Gets the album art response.
|
||||
/// </summary>
|
||||
|
|
75
MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs
Normal file
75
MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user