diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index f59b1b7df..e76dcb4a4 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Configuration;
@@ -40,6 +41,7 @@ namespace MediaBrowser.Api
private readonly ISessionManager _sessionManager;
private readonly IFileSystem _fileSystem;
+ private readonly IMediaSourceManager _mediaSourceManager;
public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1);
@@ -49,12 +51,15 @@ namespace MediaBrowser.Api
/// The logger.
/// The session manager.
/// The configuration.
- public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config, IFileSystem fileSystem)
+ /// The file system.
+ /// The media source manager.
+ public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config, IFileSystem fileSystem, IMediaSourceManager mediaSourceManager)
{
Logger = logger;
_sessionManager = sessionManager;
_config = config;
_fileSystem = fileSystem;
+ _mediaSourceManager = mediaSourceManager;
Instance = this;
}
@@ -133,6 +138,7 @@ namespace MediaBrowser.Api
///
/// The path.
/// The play session identifier.
+ /// The live stream identifier.
/// The transcoding job identifier.
/// The type.
/// The process.
@@ -142,6 +148,7 @@ namespace MediaBrowser.Api
/// TranscodingJob.
public TranscodingJob OnTranscodeBeginning(string path,
string playSessionId,
+ string liveStreamId,
string transcodingJobId,
TranscodingJobType type,
Process process,
@@ -160,7 +167,8 @@ namespace MediaBrowser.Api
DeviceId = deviceId,
CancellationTokenSource = cancellationTokenSource,
Id = transcodingJobId,
- PlaySessionId = playSessionId
+ PlaySessionId = playSessionId,
+ LiveStreamId = liveStreamId
};
_activeTranscodingJobs.Add(job);
@@ -323,7 +331,7 @@ namespace MediaBrowser.Api
}
}
- private void PingTimer(TranscodingJob job, bool isProgressCheckIn)
+ private async void PingTimer(TranscodingJob job, bool isProgressCheckIn)
{
if (job.HasExited)
{
@@ -353,6 +361,18 @@ namespace MediaBrowser.Api
{
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);
+ }
+ }
}
///
@@ -365,9 +385,11 @@ namespace MediaBrowser.Api
if (!job.HasExited && job.Type != TranscodingJobType.Progressive)
{
- if ((DateTime.UtcNow - job.LastPingDate).TotalMilliseconds < job.PingTimeout)
+ var timeSinceLastPing = (DateTime.UtcNow - job.LastPingDate).TotalMilliseconds;
+
+ if (timeSinceLastPing < job.PingTimeout)
{
- job.StartKillTimer(OnTranscodeKillTimerStopped);
+ job.StartKillTimer(OnTranscodeKillTimerStopped, job.PingTimeout);
return;
}
}
@@ -589,6 +611,11 @@ namespace MediaBrowser.Api
/// The play session identifier.
public string PlaySessionId { get; set; }
///
+ /// Gets or sets the live stream identifier.
+ ///
+ /// The live stream identifier.
+ public string LiveStreamId { get; set; }
+ ///
/// Gets or sets the path.
///
/// The path.
@@ -670,13 +697,16 @@ namespace MediaBrowser.Api
}
public void StartKillTimer(TimerCallback callback)
+ {
+ StartKillTimer(callback, PingTimeout);
+ }
+
+ public void StartKillTimer(TimerCallback callback, int intervalMs)
{
CheckHasExited();
lock (_timerLock)
{
- var intervalMs = PingTimeout;
-
if (KillTimer == null)
{
Logger.Debug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 520c3b9dc..923af3816 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -1010,6 +1010,7 @@ namespace MediaBrowser.Api.Playback
var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
state.Request.PlaySessionId,
+ state.MediaSource.LiveStreamId,
transcodingId,
TranscodingJobType,
process,
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index d16589f07..c00912115 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -309,6 +309,7 @@ namespace MediaBrowser.Controller.Library
/// The parent identifier.
/// Type of the view.
/// Name of the sort.
+ /// The unique identifier.
/// The cancellation token.
/// Task<UserView>.
Task GetNamedView(User user,
@@ -316,6 +317,7 @@ namespace MediaBrowser.Controller.Library
string parentId,
string viewType,
string sortName,
+ string uniqueId,
CancellationToken cancellationToken);
///
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index 72f917438..ac9bd6b08 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -49,7 +49,7 @@ namespace MediaBrowser.Model.Configuration
///
/// true if [enable user specific user views]; otherwise, false.
public bool EnableUserSpecificUserViews { get; set; }
-
+
///
/// Gets or sets the value pointing to the file system where the ssl certiifcate is located..
///
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index e8ef171aa..30da8bb4c 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -456,10 +456,8 @@ namespace MediaBrowser.Model.Dlna
playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue);
}
- if (!playlistItem.AudioBitrate.HasValue)
- {
- playlistItem.AudioBitrate = GetAudioBitrate(playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec);
- }
+ int audioBitrate = GetAudioBitrate(playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec);
+ playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate);
int? maxBitrateSetting = options.GetMaxBitrate();
// Honor max rate
@@ -472,9 +470,9 @@ namespace MediaBrowser.Model.Dlna
videoBitrate -= playlistItem.AudioBitrate.Value;
}
+ // Make sure the video bitrate is lower than bitrate settings but at least 64k
int currentValue = playlistItem.VideoBitrate ?? videoBitrate;
-
- playlistItem.VideoBitrate = Math.Min(videoBitrate, currentValue);
+ playlistItem.VideoBitrate = Math.Max(Math.Min(videoBitrate, currentValue), 64000);
}
}
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index f4d8ddb1a..b53e31316 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -218,7 +218,7 @@ namespace MediaBrowser.Providers.MediaInfo
await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
- FetchEmbeddedInfo(video, mediaInfo);
+ FetchEmbeddedInfo(video, mediaInfo, options);
video.IsHD = mediaStreams.Any(i => i.Type == MediaStreamType.Video && i.Width.HasValue && i.Width.Value >= 1270);
@@ -358,11 +358,13 @@ namespace MediaBrowser.Providers.MediaInfo
return _blurayExaminer.GetDiscInfo(path);
}
- private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data)
+ private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions options)
{
+ var isFullRefresh = options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;
+
if (!video.LockedFields.Contains(MetadataFields.OfficialRating))
{
- if (!string.IsNullOrWhiteSpace(data.OfficialRating))
+ if (!string.IsNullOrWhiteSpace(data.OfficialRating) || isFullRefresh)
{
video.OfficialRating = data.OfficialRating;
}
@@ -370,54 +372,75 @@ namespace MediaBrowser.Providers.MediaInfo
if (!video.LockedFields.Contains(MetadataFields.Cast))
{
- video.People.Clear();
-
- foreach (var person in data.People)
+ if (video.People.Count == 0 || isFullRefresh)
{
- video.AddPerson(new PersonInfo
+ video.People.Clear();
+
+ foreach (var person in data.People)
{
- Name = person.Name,
- Type = person.Type,
- Role = person.Role
- });
+ video.AddPerson(new PersonInfo
+ {
+ Name = person.Name,
+ Type = person.Type,
+ Role = person.Role
+ });
+ }
}
}
if (!video.LockedFields.Contains(MetadataFields.Genres))
{
- video.Genres.Clear();
-
- foreach (var genre in data.Genres)
+ if (video.Genres.Count == 0 || isFullRefresh)
{
- video.AddGenre(genre);
+ video.Genres.Clear();
+
+ foreach (var genre in data.Genres)
+ {
+ video.AddGenre(genre);
+ }
}
}
if (!video.LockedFields.Contains(MetadataFields.Studios))
{
- video.Studios.Clear();
-
- foreach (var studio in data.Studios)
+ if (video.Studios.Count == 0 || isFullRefresh)
{
- video.AddStudio(studio);
+ video.Studios.Clear();
+
+ foreach (var studio in data.Studios)
+ {
+ video.AddStudio(studio);
+ }
}
}
if (data.ProductionYear.HasValue)
{
- video.ProductionYear = data.ProductionYear;
+ if (!video.ProductionYear.HasValue || isFullRefresh)
+ {
+ video.ProductionYear = data.ProductionYear;
+ }
}
if (data.PremiereDate.HasValue)
{
- video.PremiereDate = data.PremiereDate;
+ if (!video.PremiereDate.HasValue || isFullRefresh)
+ {
+ video.PremiereDate = data.PremiereDate;
+ }
}
if (data.IndexNumber.HasValue)
{
- video.IndexNumber = data.IndexNumber;
+ if (!video.IndexNumber.HasValue || isFullRefresh)
+ {
+ video.IndexNumber = data.IndexNumber;
+ }
}
if (data.ParentIndexNumber.HasValue)
{
- video.ParentIndexNumber = data.ParentIndexNumber;
+ if (!video.ParentIndexNumber.HasValue || isFullRefresh)
+ {
+ video.ParentIndexNumber = data.ParentIndexNumber;
+ }
}
// If we don't have a ProductionYear try and get it from PremiereDate
@@ -428,7 +451,7 @@ namespace MediaBrowser.Providers.MediaInfo
if (!video.LockedFields.Contains(MetadataFields.Overview))
{
- if (!string.IsNullOrWhiteSpace(data.Overview))
+ if (string.IsNullOrWhiteSpace(video.Overview) || isFullRefresh)
{
video.Overview = data.Overview;
}
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index e99d01d54..dcdf2aa7d 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -1602,7 +1602,7 @@ namespace MediaBrowser.Server.Implementations.Library
{
if (ConfigurationManager.Configuration.EnableUserSpecificUserViews)
{
- return await GetNamedViewInternal(user, name, null, viewType, sortName, cancellationToken)
+ return await GetNamedViewInternal(user, name, null, viewType, sortName, null, cancellationToken)
.ConfigureAwait(false);
}
@@ -1662,6 +1662,7 @@ namespace MediaBrowser.Server.Implementations.Library
string parentId,
string viewType,
string sortName,
+ string uniqueId,
CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(parentId))
@@ -1669,7 +1670,7 @@ namespace MediaBrowser.Server.Implementations.Library
throw new ArgumentNullException("parentId");
}
- return GetNamedViewInternal(user, name, parentId, viewType, sortName, cancellationToken);
+ return GetNamedViewInternal(user, name, parentId, viewType, sortName, uniqueId, cancellationToken);
}
private async Task GetNamedViewInternal(User user,
@@ -1677,6 +1678,7 @@ namespace MediaBrowser.Server.Implementations.Library
string parentId,
string viewType,
string sortName,
+ string uniqueId,
CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(name))
@@ -1684,7 +1686,13 @@ namespace MediaBrowser.Server.Implementations.Library
throw new ArgumentNullException("name");
}
- var id = GetNewItemId("37_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty), typeof(UserView));
+ var idValues = "37_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty);
+ if (!string.IsNullOrWhiteSpace(uniqueId))
+ {
+ idValues += uniqueId;
+ }
+
+ var id = GetNewItemId(idValues, typeof(UserView));
var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N"));
diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
index 823599fe5..5cff9046a 100644
--- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
@@ -97,7 +97,7 @@ namespace MediaBrowser.Server.Implementations.Library
if (parents.Count > 0)
{
- list.Add(await GetUserView(parents, CollectionType.TvShows, string.Empty, user, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(parents, list, CollectionType.TvShows, string.Empty, user, cancellationToken).ConfigureAwait(false));
}
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase) || string.Equals(i.CollectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.CollectionType))
@@ -105,7 +105,7 @@ namespace MediaBrowser.Server.Implementations.Library
if (parents.Count > 0)
{
- list.Add(await GetUserView(parents, CollectionType.Music, string.Empty, user, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(parents, list, CollectionType.Music, string.Empty, user, cancellationToken).ConfigureAwait(false));
}
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.CollectionType))
@@ -113,7 +113,7 @@ namespace MediaBrowser.Server.Implementations.Library
if (parents.Count > 0)
{
- list.Add(await GetUserView(parents, CollectionType.Movies, string.Empty, user, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(parents, list, CollectionType.Movies, string.Empty, user, cancellationToken).ConfigureAwait(false));
}
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.Games, StringComparison.OrdinalIgnoreCase))
@@ -121,7 +121,7 @@ namespace MediaBrowser.Server.Implementations.Library
if (parents.Count > 0)
{
- list.Add(await GetUserView(parents, CollectionType.Games, string.Empty, user, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(parents, list, CollectionType.Games, string.Empty, user, cancellationToken).ConfigureAwait(false));
}
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase))
@@ -129,7 +129,7 @@ namespace MediaBrowser.Server.Implementations.Library
if (parents.Count > 0)
{
- list.Add(await GetUserView(parents, CollectionType.BoxSets, string.Empty, user, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(parents, list, CollectionType.BoxSets, string.Empty, user, cancellationToken).ConfigureAwait(false));
}
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
@@ -137,12 +137,12 @@ namespace MediaBrowser.Server.Implementations.Library
if (parents.Count > 0)
{
- list.Add(await GetUserView(parents, CollectionType.Playlists, string.Empty, user, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(parents, list, CollectionType.Playlists, string.Empty, user, cancellationToken).ConfigureAwait(false));
}
if (user.Configuration.DisplayFoldersView)
{
- list.Add(await GetUserView(new List(), CollectionType.Folders, "zz_" + CollectionType.Folders, user, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(new List(), list, CollectionType.Folders, "zz_" + CollectionType.Folders, user, cancellationToken).ConfigureAwait(false));
}
if (query.IncludeExternalContent)
@@ -169,7 +169,7 @@ namespace MediaBrowser.Server.Implementations.Library
if (_liveTvManager.GetEnabledUsers().Select(i => i.Id.ToString("N")).Contains(query.UserId))
{
//list.Add(await _liveTvManager.GetInternalLiveTvFolder(query.UserId, cancellationToken).ConfigureAwait(false));
- list.Add(await GetUserView(new List(), CollectionType.LiveTv, string.Empty, user, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(new List(), list, CollectionType.LiveTv, string.Empty, user, cancellationToken).ConfigureAwait(false));
}
}
@@ -190,7 +190,9 @@ namespace MediaBrowser.Server.Implementations.Library
public Task GetUserSubView(string name, string parentId, string type, User user, string sortName, CancellationToken cancellationToken)
{
- return _libraryManager.GetNamedView(user, name, parentId, type, sortName, cancellationToken);
+ var uniqueId = parentId + "subview" + type;
+
+ return _libraryManager.GetNamedView(user, name, parentId, type, sortName, uniqueId, cancellationToken);
}
public Task GetUserSubView(string parentId, string type, User user, string sortName, CancellationToken cancellationToken)
@@ -200,7 +202,7 @@ namespace MediaBrowser.Server.Implementations.Library
return GetUserSubView(name, parentId, type, user, sortName, cancellationToken);
}
- public async Task GetUserView(List parents, string viewType, string sortName, User user, CancellationToken cancellationToken)
+ public async Task GetUserView(List parents, List currentViews, string viewType, string sortName, User user, CancellationToken cancellationToken)
{
var name = _localizationManager.GetLocalizedString("ViewType" + viewType);
@@ -215,7 +217,12 @@ namespace MediaBrowser.Server.Implementations.Library
var enableRichView = !user.Configuration.PlainFolderViews.Contains(parentId.ToString("N"), StringComparer.OrdinalIgnoreCase);
- if (_config.Configuration.EnableUserSpecificUserViews || !enableRichView)
+ if (!enableRichView || currentViews.OfType().Any(i => string.Equals(i.ViewType, viewType, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase)))
+ {
+ return await GetUserView(parentId, name, viewType, enableRichView, sortName, user, cancellationToken).ConfigureAwait(false);
+ }
+
+ if (_config.Configuration.EnableUserSpecificUserViews)
{
viewType = enableRichView ? viewType : null;
var view = await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
@@ -230,16 +237,14 @@ namespace MediaBrowser.Server.Implementations.Library
return await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
}
- else
- {
- return await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
- }
+
+ return await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
}
public Task GetUserView(Guid parentId, string name, string viewType, bool enableRichView, string sortName, User user, CancellationToken cancellationToken)
{
viewType = enableRichView ? viewType : null;
- return _libraryManager.GetNamedView(user, name, parentId.ToString("N"), viewType, sortName, cancellationToken);
+ return _libraryManager.GetNamedView(user, name, parentId.ToString("N"), viewType, sortName, null, cancellationToken);
}
public List>> GetLatestItems(LatestItemsQuery request)