fixes #555 - Have clients report seek and queuing capabilities

pull/702/head
Luke Pulverenti 12 years ago
parent 14c464c28a
commit b49764dbaa

@ -186,7 +186,7 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "DatePlayed", Description = "The date the item was played (if any)", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] [ApiMember(Name = "DatePlayed", Description = "The date the item was played (if any)", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public DateTime? DatePlayed { get; set; } public DateTime? DatePlayed { get; set; }
/// <summary> /// <summary>
/// Gets or sets the id. /// Gets or sets the id.
/// </summary> /// </summary>
@ -224,6 +224,13 @@ namespace MediaBrowser.Api.UserLibrary
[Api(Description = "Reports that a user has begun playing an item")] [Api(Description = "Reports that a user has begun playing an item")]
public class OnPlaybackStart : IReturnVoid public class OnPlaybackStart : IReturnVoid
{ {
public OnPlaybackStart()
{
// Have to default these until all clients have a chance to incorporate them
CanSeek = true;
QueueableMediaTypes = "Audio,Video,Book,Game";
}
/// <summary> /// <summary>
/// Gets or sets the user id. /// Gets or sets the user id.
/// </summary> /// </summary>
@ -237,6 +244,20 @@ namespace MediaBrowser.Api.UserLibrary
/// <value>The id.</value> /// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; } public string Id { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="UpdateUserItemRating" /> is likes.
/// </summary>
/// <value><c>true</c> if likes; otherwise, <c>false</c>.</value>
[ApiMember(Name = "CanSeek", Description = "Indicates if the client can seek", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
public bool CanSeek { get; set; }
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "QueueableMediaTypes", Description = "A list of media types that can be queued from this item, comma delimited. Audio,Video,Book,Game", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)]
public string QueueableMediaTypes { get; set; }
} }
/// <summary> /// <summary>
@ -378,6 +399,8 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="libraryManager">The library manager.</param> /// <param name="libraryManager">The library manager.</param>
/// <param name="userDataRepository">The user data repository.</param> /// <param name="userDataRepository">The user data repository.</param>
/// <param name="itemRepo">The item repo.</param> /// <param name="itemRepo">The item repo.</param>
/// <param name="sessionManager">The session manager.</param>
/// <param name="dtoService">The dto service.</param>
/// <exception cref="System.ArgumentNullException">jsonSerializer</exception> /// <exception cref="System.ArgumentNullException">jsonSerializer</exception>
public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepo, ISessionManager sessionManager, IDtoService dtoService) public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepo, ISessionManager sessionManager, IDtoService dtoService)
{ {
@ -665,7 +688,17 @@ namespace MediaBrowser.Api.UserLibrary
var item = _dtoService.GetItemByDtoId(request.Id, user.Id); var item = _dtoService.GetItemByDtoId(request.Id, user.Id);
_sessionManager.OnPlaybackStart(item, GetSession().Id); var queueableMediaTypes = (request.QueueableMediaTypes ?? string.Empty);
var info = new PlaybackInfo
{
CanSeek = request.CanSeek,
Item = item,
SessionId = GetSession().Id,
QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
};
_sessionManager.OnPlaybackStart(info);
} }
/// <summary> /// <summary>

@ -5,7 +5,6 @@ using NLog.Targets;
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
namespace MediaBrowser.Common.Implementations.Logging namespace MediaBrowser.Common.Implementations.Logging
{ {
@ -193,17 +192,14 @@ namespace MediaBrowser.Common.Implementations.Logging
if (LoggerLoaded != null) if (LoggerLoaded != null)
{ {
Task.Run(() => try
{ {
try LoggerLoaded(this, EventArgs.Empty);
{ }
LoggerLoaded(this, EventArgs.Empty); catch (Exception ex)
} {
catch (Exception ex) GetLogger("Logger").ErrorException("Error in LoggerLoaded event", ex);
{ }
GetLogger("Logger").ErrorException("Error in LoggerLoaded event", ex);
}
});
} }
} }
} }

@ -54,33 +54,32 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
/// <returns>Task.</returns> /// <returns>Task.</returns>
public Task Execute(CancellationToken cancellationToken, IProgress<double> progress) public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{ {
return Task.Run(() => // Delete log files more than n days old
{ var minDateModified = DateTime.UtcNow.AddDays(-(ConfigurationManager.CommonConfiguration.LogFileRetentionDays));
// Delete log files more than n days old
var minDateModified = DateTime.UtcNow.AddDays(-(ConfigurationManager.CommonConfiguration.LogFileRetentionDays)); var filesToDelete = new DirectoryInfo(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath).EnumerateFileSystemInfos("*", SearchOption.AllDirectories)
.Where(f => f.LastWriteTimeUtc < minDateModified)
.ToList();
var filesToDelete = new DirectoryInfo(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath).EnumerateFileSystemInfos("*", SearchOption.AllDirectories) var index = 0;
.Where(f => f.LastWriteTimeUtc < minDateModified)
.ToList();
var index = 0; foreach (var file in filesToDelete)
{
double percent = index;
percent /= filesToDelete.Count;
foreach (var file in filesToDelete) progress.Report(100 * percent);
{
double percent = index;
percent /= filesToDelete.Count;
progress.Report(100 * percent); cancellationToken.ThrowIfCancellationRequested();
cancellationToken.ThrowIfCancellationRequested(); File.Delete(file.FullName);
File.Delete(file.FullName); index++;
}
index++; progress.Report(100);
}
progress.Report(100); return Task.FromResult(true);
});
} }
/// <summary> /// <summary>

@ -58,7 +58,11 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
progress.Report(0); progress.Report(0);
return Task.Run(() => LogManager.ReloadLogger(ConfigurationManager.CommonConfiguration.EnableDebugLevelLogging ? LogSeverity.Debug : LogSeverity.Info)); LogManager.ReloadLogger(ConfigurationManager.CommonConfiguration.EnableDebugLevelLogging
? LogSeverity.Debug
: LogSeverity.Info);
return Task.FromResult(true);
} }
/// <summary> /// <summary>

@ -16,7 +16,6 @@ using System.Linq;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MoreLinq;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
{ {
@ -690,7 +689,7 @@ namespace MediaBrowser.Controller.Entities
var options = new ParallelOptions var options = new ParallelOptions
{ {
MaxDegreeOfParallelism = 20 MaxDegreeOfParallelism = 10
}; };
Parallel.ForEach(nonCachedChildren, options, child => Parallel.ForEach(nonCachedChildren, options, child =>
@ -805,7 +804,7 @@ namespace MediaBrowser.Controller.Entities
foreach (var tuple in list) foreach (var tuple in list)
{ {
if (tasks.Count > 8) if (tasks.Count > 5)
{ {
await Task.WhenAll(tasks).ConfigureAwait(false); await Task.WhenAll(tasks).ConfigureAwait(false);
} }

@ -165,6 +165,7 @@
<Compile Include="Kernel.cs" /> <Compile Include="Kernel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Providers\BaseMetadataProvider.cs" /> <Compile Include="Providers\BaseMetadataProvider.cs" />
<Compile Include="Session\PlaybackInfo.cs" />
<Compile Include="Session\SessionInfo.cs" /> <Compile Include="Session\SessionInfo.cs" />
<Compile Include="Sorting\IBaseItemComparer.cs" /> <Compile Include="Sorting\IBaseItemComparer.cs" />
<Compile Include="Sorting\IUserBaseItemComparer.cs" /> <Compile Include="Sorting\IUserBaseItemComparer.cs" />

@ -47,11 +47,9 @@ namespace MediaBrowser.Controller.Session
/// <summary> /// <summary>
/// Used to report that playback has started for an item /// Used to report that playback has started for an item
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="info">The info.</param>
/// <param name="sessionId">The session id.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException"></exception> Task OnPlaybackStart(PlaybackInfo info);
Task OnPlaybackStart(BaseItem item, Guid sessionId);
/// <summary> /// <summary>
/// Used to report playback progress for an item /// Used to report playback progress for an item
@ -59,6 +57,7 @@ namespace MediaBrowser.Controller.Session
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <param name="positionTicks">The position ticks.</param> /// <param name="positionTicks">The position ticks.</param>
/// <param name="isPaused">if set to <c>true</c> [is paused].</param> /// <param name="isPaused">if set to <c>true</c> [is paused].</param>
/// <param name="isMuted">if set to <c>true</c> [is muted].</param>
/// <param name="sessionId">The session id.</param> /// <param name="sessionId">The session id.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException"></exception> /// <exception cref="System.ArgumentNullException"></exception>

@ -0,0 +1,38 @@
using MediaBrowser.Controller.Entities;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Session
{
public class PlaybackInfo
{
public PlaybackInfo()
{
QueueableMediaTypes = new List<string>();
}
/// <summary>
/// Gets or sets a value indicating whether this instance can seek.
/// </summary>
/// <value><c>true</c> if this instance can seek; otherwise, <c>false</c>.</value>
public bool CanSeek { get; set; }
/// <summary>
/// Gets or sets the queueable media types.
/// </summary>
/// <value>The queueable media types.</value>
public List<string> QueueableMediaTypes { get; set; }
/// <summary>
/// Gets or sets the item.
/// </summary>
/// <value>The item.</value>
public BaseItem Item { get; set; }
/// <summary>
/// Gets or sets the session id.
/// </summary>
/// <value>The session id.</value>
public Guid SessionId { get; set; }
}
}

@ -15,8 +15,21 @@ namespace MediaBrowser.Controller.Session
public SessionInfo() public SessionInfo()
{ {
WebSockets = new List<IWebSocketConnection>(); WebSockets = new List<IWebSocketConnection>();
QueueableMediaTypes = new List<string>();
} }
/// <summary>
/// Gets or sets a value indicating whether this instance can seek.
/// </summary>
/// <value><c>true</c> if this instance can seek; otherwise, <c>false</c>.</value>
public bool CanSeek { get; set; }
/// <summary>
/// Gets or sets the queueable media types.
/// </summary>
/// <value>The queueable media types.</value>
public List<string> QueueableMediaTypes { get; set; }
/// <summary> /// <summary>
/// Gets or sets the id. /// Gets or sets the id.
/// </summary> /// </summary>

@ -497,9 +497,11 @@ namespace MediaBrowser.Model.ApiClient
/// </summary> /// </summary>
/// <param name="itemId">The item id.</param> /// <param name="itemId">The item id.</param>
/// <param name="userId">The user id.</param> /// <param name="userId">The user id.</param>
/// <param name="isSeekable">if set to <c>true</c> [is seekable].</param>
/// <param name="queueableMediaTypes">The list of media types that the client is capable of queuing onto the playlist. See MediaType class.</param>
/// <returns>Task{UserItemDataDto}.</returns> /// <returns>Task{UserItemDataDto}.</returns>
/// <exception cref="ArgumentNullException">itemId</exception> /// <exception cref="ArgumentNullException">itemId</exception>
Task ReportPlaybackStartAsync(string itemId, string userId); Task ReportPlaybackStartAsync(string itemId, string userId, bool isSeekable, List<string> queueableMediaTypes);
/// <summary> /// <summary>
/// Reports playback progress to the server /// Reports playback progress to the server

@ -1,11 +1,24 @@
using System.ComponentModel; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Entities;
using System; using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace MediaBrowser.Model.Session namespace MediaBrowser.Model.Session
{ {
public class SessionInfoDto : INotifyPropertyChanged public class SessionInfoDto : INotifyPropertyChanged
{ {
/// <summary>
/// Gets or sets a value indicating whether this instance can seek.
/// </summary>
/// <value><c>true</c> if this instance can seek; otherwise, <c>false</c>.</value>
public bool CanSeek { get; set; }
/// <summary>
/// Gets or sets the queueable media types.
/// </summary>
/// <value>The queueable media types.</value>
public List<string> QueueableMediaTypes { get; set; }
/// <summary> /// <summary>
/// Gets or sets the id. /// Gets or sets the id.
/// </summary> /// </summary>

@ -1,5 +1,4 @@
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO; using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;

@ -23,7 +23,9 @@ namespace MediaBrowser.Providers.Music
public Task Run(IProgress<double> progress, CancellationToken cancellationToken) public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{ {
return Task.Run(() => RunInternal(progress, cancellationToken)); RunInternal(progress, cancellationToken);
return Task.FromResult(true);
} }
private void RunInternal(IProgress<double> progress, CancellationToken cancellationToken) private void RunInternal(IProgress<double> progress, CancellationToken cancellationToken)

@ -21,7 +21,9 @@ namespace MediaBrowser.Providers.TV
public Task Run(IProgress<double> progress, CancellationToken cancellationToken) public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{ {
return Task.Run(() => RunInternal(progress, cancellationToken)); RunInternal(progress, cancellationToken);
return Task.FromResult(true);
} }
private void RunInternal(IProgress<double> progress, CancellationToken cancellationToken) private void RunInternal(IProgress<double> progress, CancellationToken cancellationToken)

@ -237,7 +237,9 @@ namespace MediaBrowser.Server.Implementations.Dto
NowViewingItemId = session.NowViewingItemId, NowViewingItemId = session.NowViewingItemId,
NowViewingItemName = session.NowViewingItemName, NowViewingItemName = session.NowViewingItemName,
NowViewingItemType = session.NowViewingItemType, NowViewingItemType = session.NowViewingItemType,
ApplicationVersion = session.ApplicationVersion ApplicationVersion = session.ApplicationVersion,
CanSeek = session.CanSeek,
QueueableMediaTypes = session.QueueableMediaTypes
}; };
if (session.NowPlayingItem != null) if (session.NowPlayingItem != null)

@ -829,10 +829,6 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task.</returns> /// <returns>Task.</returns>
public async Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress) public async Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
{ {
const int maxTasks = 3;
var tasks = new List<Task>();
var people = RootFolder.RecursiveChildren var people = RootFolder.RecursiveChildren
.SelectMany(c => c.People) .SelectMany(c => c.People)
.DistinctBy(p => p.Name, StringComparer.OrdinalIgnoreCase) .DistinctBy(p => p.Name, StringComparer.OrdinalIgnoreCase)
@ -842,47 +838,27 @@ namespace MediaBrowser.Server.Implementations.Library
foreach (var person in people) foreach (var person in people)
{ {
if (tasks.Count > maxTasks) cancellationToken.ThrowIfCancellationRequested();
try
{ {
await Task.WhenAll(tasks).ConfigureAwait(false); var item = GetPerson(person.Name);
tasks.Clear();
// Safe cancellation point, when there are no pending tasks await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
} }
catch (IOException ex)
// Avoid accessing the foreach variable within the closure
var currentPerson = person;
tasks.Add(Task.Run(async () =>
{ {
cancellationToken.ThrowIfCancellationRequested(); _logger.ErrorException("Error validating IBN entry {0}", ex, person.Name);
}
try
{
var item = GetPerson(currentPerson.Name);
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (IOException ex)
{
_logger.ErrorException("Error validating IBN entry {0}", ex, currentPerson.Name);
}
// Update progress // Update progress
lock (progress) numComplete++;
{ double percent = numComplete;
numComplete++; percent /= people.Count;
double percent = numComplete;
percent /= people.Count;
progress.Report(100 * percent); progress.Report(100 * percent);
}
}));
} }
await Task.WhenAll(tasks).ConfigureAwait(false);
progress.Report(100); progress.Report(100);
_logger.Info("People validation complete"); _logger.Info("People validation complete");
@ -956,7 +932,9 @@ namespace MediaBrowser.Server.Implementations.Library
public Task ValidateMediaLibrary(IProgress<double> progress, CancellationToken cancellationToken) public Task ValidateMediaLibrary(IProgress<double> progress, CancellationToken cancellationToken)
{ {
// Just run the scheduled task so that the user can see it // Just run the scheduled task so that the user can see it
return Task.Run(() => _taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>()); _taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
return Task.FromResult(true);
} }
/// <summary> /// <summary>

@ -41,7 +41,9 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// <returns>Task.</returns> /// <returns>Task.</returns>
public Task Run(IProgress<double> progress, CancellationToken cancellationToken) public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{ {
return Task.Run(() => RunInternal(progress, cancellationToken)); RunInternal(progress, cancellationToken);
return Task.FromResult(true);
} }
private void RunInternal(IProgress<double> progress, CancellationToken cancellationToken) private void RunInternal(IProgress<double> progress, CancellationToken cancellationToken)

@ -333,17 +333,16 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// <returns>Task.</returns> /// <returns>Task.</returns>
public Task SaveCriticReviews(Guid itemId, IEnumerable<ItemReview> criticReviews) public Task SaveCriticReviews(Guid itemId, IEnumerable<ItemReview> criticReviews)
{ {
return Task.Run(() => if (!Directory.Exists(_criticReviewsPath))
{ {
if (!Directory.Exists(_criticReviewsPath)) Directory.CreateDirectory(_criticReviewsPath);
{ }
Directory.CreateDirectory(_criticReviewsPath);
}
var path = Path.Combine(_criticReviewsPath, itemId + ".json"); var path = Path.Combine(_criticReviewsPath, itemId + ".json");
_jsonSerializer.SerializeToFile(criticReviews.ToList(), path);
_jsonSerializer.SerializeToFile(criticReviews.ToList(), path); return Task.FromResult(true);
});
} }
/// <summary> /// <summary>

@ -207,21 +207,29 @@ namespace MediaBrowser.Server.Implementations.Session
/// <summary> /// <summary>
/// Used to report that playback has started for an item /// Used to report that playback has started for an item
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="info">The info.</param>
/// <param name="sessionId">The session id.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException"></exception> /// <exception cref="System.ArgumentNullException">info</exception>
public async Task OnPlaybackStart(BaseItem item, Guid sessionId) public async Task OnPlaybackStart(PlaybackInfo info)
{ {
if (item == null) if (info == null)
{ {
throw new ArgumentNullException(); throw new ArgumentNullException("info");
}
if (info.SessionId == Guid.Empty)
{
throw new ArgumentNullException("info");
} }
var session = Sessions.First(i => i.Id.Equals(sessionId)); var session = Sessions.First(i => i.Id.Equals(info.SessionId));
var item = info.Item;
UpdateNowPlayingItem(session, item, false, false); UpdateNowPlayingItem(session, item, false, false);
session.CanSeek = info.CanSeek;
session.QueueableMediaTypes = info.QueueableMediaTypes;
var key = item.GetUserDataKey(); var key = item.GetUserDataKey();
var user = session.User; var user = session.User;

@ -101,16 +101,7 @@ namespace MediaBrowser.Server.Implementations.Session
} }
else if (string.Equals(message.MessageType, "PlaybackStart", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(message.MessageType, "PlaybackStart", StringComparison.OrdinalIgnoreCase))
{ {
_logger.Debug("Received PlaybackStart message"); ReportPlaybackStart(message);
var session = _sessionManager.Sessions.FirstOrDefault(i => i.WebSockets.Contains(message.Connection));
if (session != null && session.User != null)
{
var item = _dtoService.GetItemByDtoId(message.Data);
_sessionManager.OnPlaybackStart(item, session.Id);
}
} }
else if (string.Equals(message.MessageType, "PlaybackProgress", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(message.MessageType, "PlaybackProgress", StringComparison.OrdinalIgnoreCase))
{ {
@ -170,5 +161,46 @@ namespace MediaBrowser.Server.Implementations.Session
return _trueTaskResult; return _trueTaskResult;
} }
/// <summary>
/// Reports the playback start.
/// </summary>
/// <param name="message">The message.</param>
private void ReportPlaybackStart(WebSocketMessageInfo message)
{
_logger.Debug("Received PlaybackStart message");
var session = _sessionManager.Sessions
.FirstOrDefault(i => i.WebSockets.Contains(message.Connection));
if (session != null && session.User != null)
{
var vals = message.Data.Split('|');
var item = _dtoService.GetItemByDtoId(vals[0]);
var queueableMediaTypes = string.Empty;
var canSeek = true;
if (vals.Length > 1)
{
canSeek = string.Equals(vals[1], "true", StringComparison.OrdinalIgnoreCase);
}
if (vals.Length > 2)
{
queueableMediaTypes = vals[2];
}
var info = new PlaybackInfo
{
CanSeek = canSeek,
Item = item,
SessionId = session.Id,
QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
};
_sessionManager.OnPlaybackStart(info);
}
}
} }
} }

@ -92,7 +92,9 @@ namespace MediaBrowser.Server.Implementations.WebSocket
/// <returns>Task.</returns> /// <returns>Task.</returns>
public Task SendAsync(byte[] bytes, WebSocketMessageType type, bool endOfMessage, CancellationToken cancellationToken) public Task SendAsync(byte[] bytes, WebSocketMessageType type, bool endOfMessage, CancellationToken cancellationToken)
{ {
return Task.Run(() => UserContext.Send(bytes)); UserContext.Send(bytes);
return Task.FromResult(true);
} }
/// <summary> /// <summary>

@ -3200,7 +3200,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
* @param {String} userId * @param {String} userId
* @param {String} itemId * @param {String} itemId
*/ */
self.reportPlaybackStart = function (userId, itemId) { self.reportPlaybackStart = function (userId, itemId, canSeek, queueableMediaTypes) {
if (!userId) { if (!userId) {
throw new Error("null userId"); throw new Error("null userId");
@ -3210,17 +3210,26 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
throw new Error("null itemId"); throw new Error("null itemId");
} }
canSeek = canSeek || false;
queueableMediaTypes = queueableMediaTypes || '';
if (self.isWebSocketOpen()) { if (self.isWebSocketOpen()) {
var deferred = $.Deferred(); var deferred = $.Deferred();
self.sendWebSocketMessage("PlaybackStart", itemId); var msg = [itemId, canSeek, queueableMediaTypes];
self.sendWebSocketMessage("PlaybackStart", msg.join('|'));
deferred.resolveWith(null, []); deferred.resolveWith(null, []);
return deferred.promise(); return deferred.promise();
} }
var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId); var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, {
CanSeek: canSeek,
QueueableMediaTypes: queueableMediaTypes
});
return self.ajax({ return self.ajax({
type: "POST", type: "POST",

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.175" targetFramework="net45" /> <package id="MediaBrowser.ApiClient.Javascript" version="3.0.176" targetFramework="net45" />
<package id="ServiceStack.Common" version="3.9.62" targetFramework="net45" /> <package id="ServiceStack.Common" version="3.9.62" targetFramework="net45" />
<package id="ServiceStack.Text" version="3.9.62" targetFramework="net45" /> <package id="ServiceStack.Text" version="3.9.62" targetFramework="net45" />
</packages> </packages>
Loading…
Cancel
Save