mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-11-19 20:09:03 -07:00
commit
f0f3ca8c18
@ -318,8 +318,7 @@ namespace Emby.Dlna.PlayTo
|
|||||||
|
|
||||||
CanSeek = info.MediaSource == null ? _device.Duration.HasValue : info.MediaSource.RunTimeTicks.HasValue,
|
CanSeek = info.MediaSource == null ? _device.Duration.HasValue : info.MediaSource.RunTimeTicks.HasValue,
|
||||||
|
|
||||||
PlayMethod = info.IsDirectStream ? PlayMethod.DirectStream : PlayMethod.Transcode,
|
PlayMethod = info.IsDirectStream ? PlayMethod.DirectStream : PlayMethod.Transcode
|
||||||
QueueableMediaTypes = new List<string> { mediaInfo.MediaType }
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,13 +9,10 @@ using MediaBrowser.Model.Logging;
|
|||||||
using MediaBrowser.Model.MediaInfo;
|
using MediaBrowser.Model.MediaInfo;
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.IO;
|
|
||||||
using MediaBrowser.Controller.IO;
|
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
using MediaBrowser.Model.Threading;
|
using MediaBrowser.Model.Threading;
|
||||||
@ -365,12 +362,10 @@ namespace Emby.Server.Implementations.Library
|
|||||||
private readonly Dictionary<string, LiveStreamInfo> _openStreams = new Dictionary<string, LiveStreamInfo>(StringComparer.OrdinalIgnoreCase);
|
private readonly Dictionary<string, LiveStreamInfo> _openStreams = new Dictionary<string, LiveStreamInfo>(StringComparer.OrdinalIgnoreCase);
|
||||||
private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
|
private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
public async Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, bool enableAutoClose, CancellationToken cancellationToken)
|
public async Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
enableAutoClose = false;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var tuple = GetProvider(request.OpenToken);
|
var tuple = GetProvider(request.OpenToken);
|
||||||
@ -389,8 +384,6 @@ namespace Emby.Server.Implementations.Library
|
|||||||
|
|
||||||
var info = new LiveStreamInfo
|
var info = new LiveStreamInfo
|
||||||
{
|
{
|
||||||
Date = DateTime.UtcNow,
|
|
||||||
EnableCloseTimer = enableAutoClose,
|
|
||||||
Id = mediaSource.LiveStreamId,
|
Id = mediaSource.LiveStreamId,
|
||||||
MediaSource = mediaSource,
|
MediaSource = mediaSource,
|
||||||
DirectStreamProvider = mediaSourceTuple.Item2
|
DirectStreamProvider = mediaSourceTuple.Item2
|
||||||
@ -398,11 +391,6 @@ namespace Emby.Server.Implementations.Library
|
|||||||
|
|
||||||
_openStreams[mediaSource.LiveStreamId] = info;
|
_openStreams[mediaSource.LiveStreamId] = info;
|
||||||
|
|
||||||
if (enableAutoClose)
|
|
||||||
{
|
|
||||||
StartCloseTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
var json = _jsonSerializer.SerializeToString(mediaSource);
|
var json = _jsonSerializer.SerializeToString(mediaSource);
|
||||||
_logger.Debug("Live stream opened: " + json);
|
_logger.Debug("Live stream opened: " + json);
|
||||||
var clone = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
|
var clone = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
|
||||||
@ -462,28 +450,6 @@ namespace Emby.Server.Implementations.Library
|
|||||||
return result.Item1;
|
return result.Item1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task PingLiveStream(string id, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
LiveStreamInfo info;
|
|
||||||
if (_openStreams.TryGetValue(id, out info))
|
|
||||||
{
|
|
||||||
info.Date = DateTime.UtcNow;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.Error("Failed to ping live stream {0}", id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_liveStreamSemaphore.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task CloseLiveStreamWithProvider(IMediaSourceProvider provider, string streamId)
|
private async Task CloseLiveStreamWithProvider(IMediaSourceProvider provider, string streamId)
|
||||||
{
|
{
|
||||||
_logger.Info("Closing live stream {0} with provider {1}", streamId, provider.GetType().Name);
|
_logger.Info("Closing live stream {0} with provider {1}", streamId, provider.GetType().Name);
|
||||||
@ -525,11 +491,6 @@ namespace Emby.Server.Implementations.Library
|
|||||||
|
|
||||||
await CloseLiveStreamWithProvider(tuple.Item1, tuple.Item2).ConfigureAwait(false);
|
await CloseLiveStreamWithProvider(tuple.Item1, tuple.Item2).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_openStreams.Count == 0)
|
|
||||||
{
|
|
||||||
StopCloseTimer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@ -558,66 +519,11 @@ namespace Emby.Server.Implementations.Library
|
|||||||
return new Tuple<IMediaSourceProvider, string>(provider, keyId);
|
return new Tuple<IMediaSourceProvider, string>(provider, keyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ITimer _closeTimer;
|
|
||||||
private readonly TimeSpan _openStreamMaxAge = TimeSpan.FromSeconds(180);
|
|
||||||
|
|
||||||
private void StartCloseTimer()
|
|
||||||
{
|
|
||||||
StopCloseTimer();
|
|
||||||
|
|
||||||
_closeTimer = _timerFactory.Create(CloseTimerCallback, null, _openStreamMaxAge, _openStreamMaxAge);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StopCloseTimer()
|
|
||||||
{
|
|
||||||
var timer = _closeTimer;
|
|
||||||
|
|
||||||
if (timer != null)
|
|
||||||
{
|
|
||||||
_closeTimer = null;
|
|
||||||
timer.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void CloseTimerCallback(object state)
|
|
||||||
{
|
|
||||||
List<LiveStreamInfo> infos;
|
|
||||||
await _liveStreamSemaphore.WaitAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
infos = _openStreams
|
|
||||||
.Values
|
|
||||||
.Where(i => i.EnableCloseTimer && DateTime.UtcNow - i.Date > _openStreamMaxAge)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_liveStreamSemaphore.Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var info in infos)
|
|
||||||
{
|
|
||||||
if (!info.Closed)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await CloseLiveStream(info.Id).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.ErrorException("Error closing media source", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
StopCloseTimer();
|
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -644,8 +550,6 @@ namespace Emby.Server.Implementations.Library
|
|||||||
|
|
||||||
private class LiveStreamInfo
|
private class LiveStreamInfo
|
||||||
{
|
{
|
||||||
public DateTime Date;
|
|
||||||
public bool EnableCloseTimer;
|
|
||||||
public string Id;
|
public string Id;
|
||||||
public bool Closed;
|
public bool Closed;
|
||||||
public MediaSourceInfo MediaSource;
|
public MediaSourceInfo MediaSource;
|
||||||
|
@ -190,7 +190,7 @@ namespace Emby.Server.Implementations.LiveTv
|
|||||||
|
|
||||||
else if (width >= 1260)
|
else if (width >= 1260)
|
||||||
{
|
{
|
||||||
videoStream.BitRate = 3000000;
|
videoStream.BitRate = 4000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (width >= 700)
|
else if (width >= 700)
|
||||||
|
@ -197,6 +197,8 @@ namespace Emby.Server.Implementations.Session
|
|||||||
_logger.ErrorException("Error disposing session controller", ex);
|
_logger.ErrorException("Error disposing session controller", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -308,10 +310,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the now playing item id.
|
/// Updates the now playing item id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="session">The session.</param>
|
private async Task UpdateNowPlayingItem(SessionInfo session, PlaybackProgressInfo info, BaseItem libraryItem, bool updateLastCheckInTime)
|
||||||
/// <param name="info">The information.</param>
|
|
||||||
/// <param name="libraryItem">The library item.</param>
|
|
||||||
private async Task UpdateNowPlayingItem(SessionInfo session, PlaybackProgressInfo info, BaseItem libraryItem)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(info.MediaSourceId))
|
if (string.IsNullOrWhiteSpace(info.MediaSourceId))
|
||||||
{
|
{
|
||||||
@ -350,7 +349,11 @@ namespace Emby.Server.Implementations.Session
|
|||||||
|
|
||||||
session.NowPlayingItem = info.Item;
|
session.NowPlayingItem = info.Item;
|
||||||
session.LastActivityDate = DateTime.UtcNow;
|
session.LastActivityDate = DateTime.UtcNow;
|
||||||
session.LastPlaybackCheckIn = DateTime.UtcNow;
|
|
||||||
|
if (updateLastCheckInTime)
|
||||||
|
{
|
||||||
|
session.LastPlaybackCheckIn = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
|
||||||
session.PlayState.IsPaused = info.IsPaused;
|
session.PlayState.IsPaused = info.IsPaused;
|
||||||
session.PlayState.PositionTicks = info.PositionTicks;
|
session.PlayState.PositionTicks = info.PositionTicks;
|
||||||
@ -415,7 +418,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
|
|
||||||
if (!_activeConnections.TryGetValue(key, out sessionInfo))
|
if (!_activeConnections.TryGetValue(key, out sessionInfo))
|
||||||
{
|
{
|
||||||
sessionInfo = new SessionInfo
|
sessionInfo = new SessionInfo(this, _logger)
|
||||||
{
|
{
|
||||||
Client = appName,
|
Client = appName,
|
||||||
DeviceId = deviceId,
|
DeviceId = deviceId,
|
||||||
@ -602,14 +605,14 @@ namespace Emby.Server.Implementations.Session
|
|||||||
? null
|
? null
|
||||||
: GetNowPlayingItem(session, info.ItemId);
|
: GetNowPlayingItem(session, info.ItemId);
|
||||||
|
|
||||||
await UpdateNowPlayingItem(session, info, libraryItem).ConfigureAwait(false);
|
await UpdateNowPlayingItem(session, info, libraryItem, true).ConfigureAwait(false);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(session.DeviceId) && info.PlayMethod != PlayMethod.Transcode)
|
if (!string.IsNullOrEmpty(session.DeviceId) && info.PlayMethod != PlayMethod.Transcode)
|
||||||
{
|
{
|
||||||
ClearTranscodingInfo(session.DeviceId);
|
ClearTranscodingInfo(session.DeviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
session.QueueableMediaTypes = info.QueueableMediaTypes;
|
session.StartAutomaticProgress(_timerFactory, info);
|
||||||
|
|
||||||
var users = GetUsers(session);
|
var users = GetUsers(session);
|
||||||
|
|
||||||
@ -668,14 +671,15 @@ namespace Emby.Server.Implementations.Session
|
|||||||
await _userDataManager.SaveUserData(userId, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None).ConfigureAwait(false);
|
await _userDataManager.SaveUserData(userId, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task OnPlaybackProgress(PlaybackProgressInfo info)
|
||||||
|
{
|
||||||
|
return OnPlaybackProgress(info, false);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to report playback progress for an item
|
/// Used to report playback progress for an item
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="info">The info.</param>
|
public async Task OnPlaybackProgress(PlaybackProgressInfo info, bool isAutomated)
|
||||||
/// <returns>Task.</returns>
|
|
||||||
/// <exception cref="System.ArgumentNullException"></exception>
|
|
||||||
/// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception>
|
|
||||||
public async Task OnPlaybackProgress(PlaybackProgressInfo info)
|
|
||||||
{
|
{
|
||||||
if (info == null)
|
if (info == null)
|
||||||
{
|
{
|
||||||
@ -688,7 +692,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
? null
|
? null
|
||||||
: GetNowPlayingItem(session, info.ItemId);
|
: GetNowPlayingItem(session, info.ItemId);
|
||||||
|
|
||||||
await UpdateNowPlayingItem(session, info, libraryItem).ConfigureAwait(false);
|
await UpdateNowPlayingItem(session, info, libraryItem, !isAutomated).ConfigureAwait(false);
|
||||||
|
|
||||||
var users = GetUsers(session);
|
var users = GetUsers(session);
|
||||||
|
|
||||||
@ -700,18 +704,6 @@ namespace Emby.Server.Implementations.Session
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(info.LiveStreamId))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await _mediaSourceManager.PingLiveStream(info.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.ErrorException("Error closing live stream", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EventHelper.FireEventIfNotNull(PlaybackProgress, this, new PlaybackProgressEventArgs
|
EventHelper.FireEventIfNotNull(PlaybackProgress, this, new PlaybackProgressEventArgs
|
||||||
{
|
{
|
||||||
Item = libraryItem,
|
Item = libraryItem,
|
||||||
@ -727,6 +719,11 @@ namespace Emby.Server.Implementations.Session
|
|||||||
|
|
||||||
}, _logger);
|
}, _logger);
|
||||||
|
|
||||||
|
if (!isAutomated)
|
||||||
|
{
|
||||||
|
session.StartAutomaticProgress(_timerFactory, info);
|
||||||
|
}
|
||||||
|
|
||||||
StartIdleCheckTimer();
|
StartIdleCheckTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -788,6 +785,8 @@ namespace Emby.Server.Implementations.Session
|
|||||||
|
|
||||||
var session = GetSession(info.SessionId);
|
var session = GetSession(info.SessionId);
|
||||||
|
|
||||||
|
session.StopAutomaticProgress();
|
||||||
|
|
||||||
var libraryItem = string.IsNullOrWhiteSpace(info.ItemId)
|
var libraryItem = string.IsNullOrWhiteSpace(info.ItemId)
|
||||||
? null
|
? null
|
||||||
: GetNowPlayingItem(session, info.ItemId);
|
: GetNowPlayingItem(session, info.ItemId);
|
||||||
@ -1009,19 +1008,9 @@ namespace Emby.Server.Implementations.Session
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.PlayCommand != PlayCommand.PlayNow)
|
if (items.Any(i => !session.PlayableMediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
if (items.Any(i => !session.QueueableMediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase)))
|
throw new ArgumentException(string.Format("{0} is unable to play the requested media type.", session.DeviceName ?? session.Id));
|
||||||
{
|
|
||||||
throw new ArgumentException(string.Format("{0} is unable to queue the requested media type.", session.DeviceName ?? session.Id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (items.Any(i => !session.PlayableMediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase)))
|
|
||||||
{
|
|
||||||
throw new ArgumentException(string.Format("{0} is unable to play the requested media type.", session.DeviceName ?? session.Id));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user != null && command.ItemIds.Length == 1 && user.Configuration.EnableNextEpisodeAutoPlay)
|
if (user != null && command.ItemIds.Length == 1 && user.Configuration.EnableNextEpisodeAutoPlay)
|
||||||
@ -1601,7 +1590,6 @@ namespace Emby.Server.Implementations.Session
|
|||||||
LastActivityDate = session.LastActivityDate,
|
LastActivityDate = session.LastActivityDate,
|
||||||
NowViewingItem = session.NowViewingItem,
|
NowViewingItem = session.NowViewingItem,
|
||||||
ApplicationVersion = session.ApplicationVersion,
|
ApplicationVersion = session.ApplicationVersion,
|
||||||
QueueableMediaTypes = session.QueueableMediaTypes,
|
|
||||||
PlayableMediaTypes = session.PlayableMediaTypes,
|
PlayableMediaTypes = session.PlayableMediaTypes,
|
||||||
AdditionalUsers = session.AdditionalUsers,
|
AdditionalUsers = session.AdditionalUsers,
|
||||||
SupportedCommands = session.SupportedCommands,
|
SupportedCommands = session.SupportedCommands,
|
||||||
|
@ -289,7 +289,6 @@ namespace Emby.Server.Implementations.Session
|
|||||||
|
|
||||||
var itemId = vals[0];
|
var itemId = vals[0];
|
||||||
|
|
||||||
var queueableMediaTypes = string.Empty;
|
|
||||||
var canSeek = true;
|
var canSeek = true;
|
||||||
|
|
||||||
if (vals.Length > 1)
|
if (vals.Length > 1)
|
||||||
@ -298,15 +297,14 @@ namespace Emby.Server.Implementations.Session
|
|||||||
}
|
}
|
||||||
if (vals.Length > 2)
|
if (vals.Length > 2)
|
||||||
{
|
{
|
||||||
queueableMediaTypes = vals[2];
|
// vals[2] used to be QueueableMediaTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
var info = new PlaybackStartInfo
|
var info = new PlaybackStartInfo
|
||||||
{
|
{
|
||||||
CanSeek = canSeek,
|
CanSeek = canSeek,
|
||||||
ItemId = itemId,
|
ItemId = itemId,
|
||||||
SessionId = session.Id,
|
SessionId = session.Id
|
||||||
QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (vals.Length > 3)
|
if (vals.Length > 3)
|
||||||
|
@ -426,18 +426,6 @@ namespace MediaBrowser.Api
|
|||||||
{
|
{
|
||||||
job.ChangeKillTimerIfStarted();
|
job.ChangeKillTimerIfStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(job.LiveStreamId))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await _mediaSourceManager.PingLiveStream(job.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.ErrorException("Error closing live stream", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -169,7 +169,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
{
|
{
|
||||||
OpenToken = state.MediaSource.OpenToken
|
OpenToken = state.MediaSource.OpenToken
|
||||||
|
|
||||||
}, false, cancellationTokenSource.Token).ConfigureAwait(false);
|
}, cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
|
|
||||||
EncodingHelper.AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, state.RequestedUrl);
|
EncodingHelper.AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, state.RequestedUrl);
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
{
|
{
|
||||||
var authInfo = _authContext.GetAuthorizationInfo(Request);
|
var authInfo = _authContext.GetAuthorizationInfo(Request);
|
||||||
|
|
||||||
var result = await _mediaSourceManager.OpenLiveStream(request, true, CancellationToken.None).ConfigureAwait(false);
|
var result = await _mediaSourceManager.OpenLiveStream(request, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
var profile = request.DeviceProfile;
|
var profile = request.DeviceProfile;
|
||||||
if (profile == null)
|
if (profile == null)
|
||||||
|
@ -12,7 +12,6 @@ using MediaBrowser.Controller.Library;
|
|||||||
namespace MediaBrowser.Api
|
namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
[Route("/Users/{UserId}/Suggestions", "GET", Summary = "Gets items based on a query.")]
|
[Route("/Users/{UserId}/Suggestions", "GET", Summary = "Gets items based on a query.")]
|
||||||
[Route("/Users/{UserId}/Suggestions", "POST", Summary = "Gets items based on a query.")]
|
|
||||||
public class GetSuggestedItems : IReturn<QueryResult<BaseItem>>
|
public class GetSuggestedItems : IReturn<QueryResult<BaseItem>>
|
||||||
{
|
{
|
||||||
public string MediaType { get; set; }
|
public string MediaType { get; set; }
|
||||||
@ -21,7 +20,6 @@ namespace MediaBrowser.Api
|
|||||||
public bool EnableTotalRecordCount { get; set; }
|
public bool EnableTotalRecordCount { get; set; }
|
||||||
public int? StartIndex { get; set; }
|
public int? StartIndex { get; set; }
|
||||||
public int? Limit { get; set; }
|
public int? Limit { get; set; }
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
public string[] GetMediaTypes()
|
public string[] GetMediaTypes()
|
||||||
{
|
{
|
||||||
@ -56,13 +54,6 @@ namespace MediaBrowser.Api
|
|||||||
return ToOptimizedResult(result);
|
return ToOptimizedResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<object> Post(GetSuggestedItems request)
|
|
||||||
{
|
|
||||||
var result = await GetResultItems(request).ConfigureAwait(false);
|
|
||||||
|
|
||||||
return ToOptimizedResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<QueryResult<BaseItemDto>> GetResultItems(GetSuggestedItems request)
|
private async Task<QueryResult<BaseItemDto>> GetResultItems(GetSuggestedItems request)
|
||||||
{
|
{
|
||||||
var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
|
var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
|
||||||
@ -86,29 +77,6 @@ namespace MediaBrowser.Api
|
|||||||
|
|
||||||
private QueryResult<BaseItem> GetItems(GetSuggestedItems request, User user, DtoOptions dtoOptions)
|
private QueryResult<BaseItem> GetItems(GetSuggestedItems request, User user, DtoOptions dtoOptions)
|
||||||
{
|
{
|
||||||
BaseItem similarToItem = null;
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(request.Name))
|
|
||||||
{
|
|
||||||
// get item by name, then get similar items from that
|
|
||||||
similarToItem = _libraryManager.GetItemList(new InternalItemsQuery(user)
|
|
||||||
{
|
|
||||||
SortBy = new string[] {ItemSortBy.Random},
|
|
||||||
MediaTypes = request.GetMediaTypes(),
|
|
||||||
IncludeItemTypes = request.GetIncludeItemTypes(),
|
|
||||||
IsVirtualItem = false,
|
|
||||||
Name = request.Name,
|
|
||||||
Recursive = true,
|
|
||||||
Limit = 1
|
|
||||||
|
|
||||||
}).FirstOrDefault();
|
|
||||||
|
|
||||||
if (similarToItem == null)
|
|
||||||
{
|
|
||||||
return new QueryResult<BaseItem>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _libraryManager.GetItemsResult(new InternalItemsQuery(user)
|
return _libraryManager.GetItemsResult(new InternalItemsQuery(user)
|
||||||
{
|
{
|
||||||
SortBy = new string[] { ItemSortBy.Random },
|
SortBy = new string[] { ItemSortBy.Random },
|
||||||
@ -119,8 +87,7 @@ namespace MediaBrowser.Api
|
|||||||
Limit = request.Limit,
|
Limit = request.Limit,
|
||||||
DtoOptions = dtoOptions,
|
DtoOptions = dtoOptions,
|
||||||
EnableTotalRecordCount = request.EnableTotalRecordCount,
|
EnableTotalRecordCount = request.EnableTotalRecordCount,
|
||||||
Recursive = true,
|
Recursive = true
|
||||||
SimilarTo = similarToItem
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,6 @@ namespace MediaBrowser.Api
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Route("/Shows/{Id}/Episodes", "GET", Summary = "Gets episodes for a tv season")]
|
[Route("/Shows/{Id}/Episodes", "GET", Summary = "Gets episodes for a tv season")]
|
||||||
[Route("/Shows/Episodes", "POST", Summary = "Gets episodes for a tv season")]
|
|
||||||
public class GetEpisodes : IReturn<ItemsResult>, IHasItemFields, IHasDtoOptions
|
public class GetEpisodes : IReturn<ItemsResult>, IHasItemFields, IHasDtoOptions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -200,12 +199,9 @@ namespace MediaBrowser.Api
|
|||||||
|
|
||||||
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
|
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
|
||||||
public bool? EnableUserData { get; set; }
|
public bool? EnableUserData { get; set; }
|
||||||
|
|
||||||
public string SeriesName { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/Shows/{Id}/Seasons", "GET", Summary = "Gets seasons for a tv series")]
|
[Route("/Shows/{Id}/Seasons", "GET", Summary = "Gets seasons for a tv series")]
|
||||||
[Route("/Shows/Seasons", "POST", Summary = "Gets seasons for a tv series")]
|
|
||||||
public class GetSeasons : IReturn<ItemsResult>, IHasItemFields, IHasDtoOptions
|
public class GetSeasons : IReturn<ItemsResult>, IHasItemFields, IHasDtoOptions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -248,8 +244,6 @@ namespace MediaBrowser.Api
|
|||||||
|
|
||||||
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
|
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
|
||||||
public bool? EnableUserData { get; set; }
|
public bool? EnableUserData { get; set; }
|
||||||
|
|
||||||
public string SeriesName { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -431,7 +425,7 @@ namespace MediaBrowser.Api
|
|||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(request.UserId);
|
var user = _userManager.GetUserById(request.UserId);
|
||||||
|
|
||||||
var series = GetSeries(request.Id, request.SeriesName, user);
|
var series = GetSeries(request.Id, user);
|
||||||
|
|
||||||
if (series == null)
|
if (series == null)
|
||||||
{
|
{
|
||||||
@ -459,33 +453,13 @@ namespace MediaBrowser.Api
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<object> Post(GetSeasons request)
|
private Series GetSeries(string seriesId, User user)
|
||||||
{
|
|
||||||
return Get(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<object> Post(GetEpisodes request)
|
|
||||||
{
|
|
||||||
return Get(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Series GetSeries(string seriesId, string seriesName, User user)
|
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(seriesId))
|
if (!string.IsNullOrWhiteSpace(seriesId))
|
||||||
{
|
{
|
||||||
return _libraryManager.GetItemById(seriesId) as Series;
|
return _libraryManager.GetItemById(seriesId) as Series;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(seriesName))
|
|
||||||
{
|
|
||||||
return _libraryManager.GetItemList(new InternalItemsQuery(user)
|
|
||||||
{
|
|
||||||
Name = seriesName,
|
|
||||||
IncludeItemTypes = new string[] { typeof(Series).Name }
|
|
||||||
|
|
||||||
}).OfType<Series>().FirstOrDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,7 +482,7 @@ namespace MediaBrowser.Api
|
|||||||
}
|
}
|
||||||
else if (request.Season.HasValue)
|
else if (request.Season.HasValue)
|
||||||
{
|
{
|
||||||
var series = GetSeries(request.Id, request.SeriesName, user);
|
var series = GetSeries(request.Id, user);
|
||||||
|
|
||||||
if (series == null)
|
if (series == null)
|
||||||
{
|
{
|
||||||
@ -528,7 +502,7 @@ namespace MediaBrowser.Api
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var series = GetSeries(request.Id, request.SeriesName, user);
|
var series = GetSeries(request.Id, user);
|
||||||
|
|
||||||
if (series == null)
|
if (series == null)
|
||||||
{
|
{
|
||||||
|
@ -109,13 +109,6 @@ namespace MediaBrowser.Api.UserLibrary
|
|||||||
[ApiMember(Name = "CanSeek", Description = "Indicates if the client can seek", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
|
[ApiMember(Name = "CanSeek", Description = "Indicates if the client can seek", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
|
||||||
public bool CanSeek { get; set; }
|
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; }
|
|
||||||
|
|
||||||
[ApiMember(Name = "AudioStreamIndex", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
|
[ApiMember(Name = "AudioStreamIndex", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
|
||||||
public int? AudioStreamIndex { get; set; }
|
public int? AudioStreamIndex { get; set; }
|
||||||
|
|
||||||
@ -292,13 +285,10 @@ namespace MediaBrowser.Api.UserLibrary
|
|||||||
/// <param name="request">The request.</param>
|
/// <param name="request">The request.</param>
|
||||||
public void Post(OnPlaybackStart request)
|
public void Post(OnPlaybackStart request)
|
||||||
{
|
{
|
||||||
var queueableMediaTypes = request.QueueableMediaTypes ?? string.Empty;
|
|
||||||
|
|
||||||
Post(new ReportPlaybackStart
|
Post(new ReportPlaybackStart
|
||||||
{
|
{
|
||||||
CanSeek = request.CanSeek,
|
CanSeek = request.CanSeek,
|
||||||
ItemId = request.Id,
|
ItemId = request.Id,
|
||||||
QueueableMediaTypes = queueableMediaTypes.Split(',').ToList(),
|
|
||||||
MediaSourceId = request.MediaSourceId,
|
MediaSourceId = request.MediaSourceId,
|
||||||
AudioStreamIndex = request.AudioStreamIndex,
|
AudioStreamIndex = request.AudioStreamIndex,
|
||||||
SubtitleStreamIndex = request.SubtitleStreamIndex,
|
SubtitleStreamIndex = request.SubtitleStreamIndex,
|
||||||
|
@ -68,10 +68,9 @@ namespace MediaBrowser.Controller.Library
|
|||||||
/// Opens the media source.
|
/// Opens the media source.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request">The request.</param>
|
/// <param name="request">The request.</param>
|
||||||
/// <param name="enableAutoClose">if set to <c>true</c> [enable automatic close].</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task<MediaSourceInfo>.</returns>
|
/// <returns>Task<MediaSourceInfo>.</returns>
|
||||||
Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, bool enableAutoClose, CancellationToken cancellationToken);
|
Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the live stream.
|
/// Gets the live stream.
|
||||||
@ -83,14 +82,6 @@ namespace MediaBrowser.Controller.Library
|
|||||||
|
|
||||||
Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStreamWithDirectStreamProvider(string id, CancellationToken cancellationToken);
|
Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStreamWithDirectStreamProvider(string id, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pings the media source.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">The live stream identifier.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>Task.</returns>
|
|
||||||
Task PingLiveStream(string id, CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Closes the media source.
|
/// Closes the media source.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -99,6 +99,8 @@ namespace MediaBrowser.Controller.Session
|
|||||||
/// <exception cref="System.ArgumentNullException"></exception>
|
/// <exception cref="System.ArgumentNullException"></exception>
|
||||||
Task OnPlaybackProgress(PlaybackProgressInfo info);
|
Task OnPlaybackProgress(PlaybackProgressInfo info);
|
||||||
|
|
||||||
|
Task OnPlaybackProgress(PlaybackProgressInfo info, bool isAutomated);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to report that playback has ended for an item
|
/// Used to report that playback has ended for an item
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -4,17 +4,23 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
|
using MediaBrowser.Model.Logging;
|
||||||
|
using MediaBrowser.Model.Threading;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Session
|
namespace MediaBrowser.Controller.Session
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class SessionInfo
|
/// Class SessionInfo
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SessionInfo
|
public class SessionInfo : IDisposable
|
||||||
{
|
{
|
||||||
public SessionInfo()
|
private ISessionManager _sessionManager;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
public SessionInfo(ISessionManager sessionManager, ILogger logger)
|
||||||
{
|
{
|
||||||
QueueableMediaTypes = new List<string>();
|
_sessionManager = sessionManager;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
AdditionalUsers = new List<SessionUserInfo>();
|
AdditionalUsers = new List<SessionUserInfo>();
|
||||||
PlayState = new PlayerStateInfo();
|
PlayState = new PlayerStateInfo();
|
||||||
@ -32,12 +38,6 @@ namespace MediaBrowser.Controller.Session
|
|||||||
/// <value>The remote end point.</value>
|
/// <value>The remote end point.</value>
|
||||||
public string RemoteEndPoint { get; set; }
|
public string RemoteEndPoint { 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 playable media types.
|
/// Gets or sets the playable media types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -196,5 +196,88 @@ namespace MediaBrowser.Controller.Session
|
|||||||
{
|
{
|
||||||
return (UserId ?? Guid.Empty) == userId || AdditionalUsers.Any(i => userId == new Guid(i.UserId));
|
return (UserId ?? Guid.Empty) == userId || AdditionalUsers.Any(i => userId == new Guid(i.UserId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly object _progressLock = new object();
|
||||||
|
private ITimer _progressTimer;
|
||||||
|
private PlaybackProgressInfo _lastProgressInfo;
|
||||||
|
|
||||||
|
public void StartAutomaticProgress(ITimerFactory timerFactory, PlaybackProgressInfo progressInfo)
|
||||||
|
{
|
||||||
|
lock (_progressLock)
|
||||||
|
{
|
||||||
|
_lastProgressInfo = progressInfo;
|
||||||
|
|
||||||
|
if (_progressTimer == null)
|
||||||
|
{
|
||||||
|
_progressTimer = timerFactory.Create(OnProgressTimerCallback, null, 1000, 1000);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_progressTimer.Change(1000, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1 second
|
||||||
|
private const long ProgressIncrement = 10000000;
|
||||||
|
|
||||||
|
private async void OnProgressTimerCallback(object state)
|
||||||
|
{
|
||||||
|
var progressInfo = _lastProgressInfo;
|
||||||
|
if (progressInfo == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (progressInfo.IsPaused)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var positionTicks = progressInfo.PositionTicks ?? 0;
|
||||||
|
if (positionTicks < 0)
|
||||||
|
{
|
||||||
|
positionTicks = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newPositionTicks = positionTicks + ProgressIncrement;
|
||||||
|
var item = progressInfo.Item;
|
||||||
|
long? runtimeTicks = item == null ? null : item.RunTimeTicks;
|
||||||
|
|
||||||
|
// Don't report beyond the runtime
|
||||||
|
if (runtimeTicks.HasValue && newPositionTicks >= runtimeTicks.Value)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
progressInfo.PositionTicks = newPositionTicks;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _sessionManager.OnPlaybackProgress(progressInfo, true).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.ErrorException("Error reporting playback progress", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopAutomaticProgress()
|
||||||
|
{
|
||||||
|
lock (_progressLock)
|
||||||
|
{
|
||||||
|
if (_progressTimer != null)
|
||||||
|
{
|
||||||
|
_progressTimer.Dispose();
|
||||||
|
_progressTimer = null;
|
||||||
|
}
|
||||||
|
_lastProgressInfo = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
StopAutomaticProgress();
|
||||||
|
_sessionManager = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -356,7 +356,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
{
|
{
|
||||||
OpenToken = state.MediaSource.OpenToken
|
OpenToken = state.MediaSource.OpenToken
|
||||||
|
|
||||||
}, false, cancellationToken).ConfigureAwait(false);
|
}, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
EncodingHelper.AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, null);
|
EncodingHelper.AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, null);
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Session
|
namespace MediaBrowser.Model.Session
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -7,15 +6,5 @@ namespace MediaBrowser.Model.Session
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class PlaybackStartInfo : PlaybackProgressInfo
|
public class PlaybackStartInfo : PlaybackProgressInfo
|
||||||
{
|
{
|
||||||
public PlaybackStartInfo()
|
|
||||||
{
|
|
||||||
QueueableMediaTypes = new List<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the queueable media types.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The queueable media types.</value>
|
|
||||||
public List<string> QueueableMediaTypes { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,12 +14,6 @@ namespace MediaBrowser.Model.Session
|
|||||||
/// <value>The supported commands.</value>
|
/// <value>The supported commands.</value>
|
||||||
public List<string> SupportedCommands { get; set; }
|
public List<string> SupportedCommands { 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 playable media types.
|
/// Gets or sets the playable media types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -119,7 +113,6 @@ namespace MediaBrowser.Model.Session
|
|||||||
AdditionalUsers = new List<SessionUserInfo>();
|
AdditionalUsers = new List<SessionUserInfo>();
|
||||||
|
|
||||||
PlayableMediaTypes = new List<string>();
|
PlayableMediaTypes = new List<string>();
|
||||||
QueueableMediaTypes = new List<string>();
|
|
||||||
SupportedCommands = new List<string>();
|
SupportedCommands = new List<string>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
[assembly: AssemblyVersion("3.2.12.7")]
|
[assembly: AssemblyVersion("3.2.12.8")]
|
||||||
|
Loading…
Reference in New Issue
Block a user