mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
fixes #555 - Have clients report seek and queuing capabilities
This commit is contained in:
parent
14c464c28a
commit
b49764dbaa
@ -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
|
||||||
{
|
{
|
||||||
@ -192,8 +191,6 @@ namespace MediaBrowser.Common.Implementations.Logging
|
|||||||
LogSeverity = level;
|
LogSeverity = level;
|
||||||
|
|
||||||
if (LoggerLoaded != null)
|
if (LoggerLoaded != null)
|
||||||
{
|
|
||||||
Task.Run(() =>
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -203,7 +200,6 @@ namespace MediaBrowser.Common.Implementations.Logging
|
|||||||
{
|
{
|
||||||
GetLogger("Logger").ErrorException("Error in LoggerLoaded event", ex);
|
GetLogger("Logger").ErrorException("Error in LoggerLoaded event", ex);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,8 +53,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
|||||||
/// <param name="progress">The progress.</param>
|
/// <param name="progress">The progress.</param>
|
||||||
/// <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
|
// Delete log files more than n days old
|
||||||
var minDateModified = DateTime.UtcNow.AddDays(-(ConfigurationManager.CommonConfiguration.LogFileRetentionDays));
|
var minDateModified = DateTime.UtcNow.AddDays(-(ConfigurationManager.CommonConfiguration.LogFileRetentionDays));
|
||||||
@ -80,7 +78,8 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
|||||||
}
|
}
|
||||||
|
|
||||||
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>
|
||||||
|
38
MediaBrowser.Controller/Session/PlaybackInfo.cs
Normal file
38
MediaBrowser.Controller/Session/PlaybackInfo.cs
Normal file
@ -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)
|
||||||
@ -841,47 +837,27 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||||||
var numComplete = 0;
|
var numComplete = 0;
|
||||||
|
|
||||||
foreach (var person in people)
|
foreach (var person in people)
|
||||||
{
|
|
||||||
if (tasks.Count > maxTasks)
|
|
||||||
{
|
|
||||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
|
||||||
tasks.Clear();
|
|
||||||
|
|
||||||
// Safe cancellation point, when there are no pending tasks
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid accessing the foreach variable within the closure
|
|
||||||
var currentPerson = person;
|
|
||||||
|
|
||||||
tasks.Add(Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var item = GetPerson(currentPerson.Name);
|
var item = GetPerson(person.Name);
|
||||||
|
|
||||||
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
|
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (IOException ex)
|
catch (IOException ex)
|
||||||
{
|
{
|
||||||
_logger.ErrorException("Error validating IBN entry {0}", ex, currentPerson.Name);
|
_logger.ErrorException("Error validating IBN entry {0}", ex, person.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update progress
|
// Update progress
|
||||||
lock (progress)
|
|
||||||
{
|
|
||||||
numComplete++;
|
numComplete++;
|
||||||
double percent = numComplete;
|
double percent = numComplete;
|
||||||
percent /= people.Count;
|
percent /= people.Count;
|
||||||
|
|
||||||
progress.Report(100 * percent);
|
progress.Report(100 * percent);
|
||||||
}
|
}
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
|
||||||
|
|
||||||
progress.Report(100);
|
progress.Report(100);
|
||||||
|
|
||||||
@ -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)
|
||||||
|
@ -332,8 +332,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||||||
/// <param name="criticReviews">The critic reviews.</param>
|
/// <param name="criticReviews">The critic reviews.</param>
|
||||||
/// <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))
|
||||||
{
|
{
|
||||||
@ -343,7 +341,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||||||
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…
x
Reference in New Issue
Block a user