diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs
index 6d600c646b..55c6f6455e 100644
--- a/Emby.Drawing/ImageProcessor.cs
+++ b/Emby.Drawing/ImageProcessor.cs
@@ -158,22 +158,19 @@ namespace Emby.Drawing
}
var dateModified = options.Image.DateModified;
- var length = options.Image.Length;
if (options.CropWhiteSpace)
{
- var tuple = await GetWhitespaceCroppedImage(originalImagePath, dateModified, length).ConfigureAwait(false);
+ var tuple = await GetWhitespaceCroppedImage(originalImagePath, dateModified).ConfigureAwait(false);
originalImagePath = tuple.Item1;
dateModified = tuple.Item2;
- length = tuple.Item3;
}
if (options.Enhancers.Count > 0)
{
var tuple = await GetEnhancedImage(new ItemImageInfo
{
- Length = length,
DateModified = dateModified,
Type = options.Image.Type,
Path = originalImagePath
@@ -182,7 +179,6 @@ namespace Emby.Drawing
originalImagePath = tuple.Item1;
dateModified = tuple.Item2;
- length = tuple.Item3;
}
var originalImageSize = GetImageSize(originalImagePath, dateModified);
@@ -199,7 +195,7 @@ namespace Emby.Drawing
var quality = options.Quality ?? 90;
var outputFormat = GetOutputFormat(options.OutputFormat);
- var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, length, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.BackgroundColor);
+ var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.BackgroundColor);
var semaphore = GetLock(cacheFilePath);
@@ -240,11 +236,10 @@ namespace Emby.Drawing
///
/// Crops whitespace from an image, caches the result, and returns the cached path
///
- private async Task> GetWhitespaceCroppedImage(string originalImagePath, DateTime dateModified, long length)
+ private async Task> GetWhitespaceCroppedImage(string originalImagePath, DateTime dateModified)
{
var name = originalImagePath;
name += "datemodified=" + dateModified.Ticks;
- name += "length=" + length;
var croppedImagePath = GetCachePath(CroppedWhitespaceImageCachePath, name, Path.GetExtension(originalImagePath));
@@ -270,7 +265,7 @@ namespace Emby.Drawing
// We have to have a catch-all here because some of the .net image methods throw a plain old Exception
_logger.ErrorException("Error cropping image {0}", ex, originalImagePath);
- return new Tuple(originalImagePath, dateModified, length);
+ return new Tuple(originalImagePath, dateModified);
}
finally
{
@@ -280,11 +275,9 @@ namespace Emby.Drawing
return GetResult(croppedImagePath);
}
- private Tuple GetResult(string path)
+ private Tuple GetResult(string path)
{
- var file = new FileInfo(path);
-
- return new Tuple(path, _fileSystem.GetLastWriteTimeUtc(file), file.Length);
+ return new Tuple(path, _fileSystem.GetLastWriteTimeUtc(path));
}
///
@@ -295,7 +288,7 @@ namespace Emby.Drawing
///
/// Gets the cache file path based on a set of parameters
///
- private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, long length, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, string backgroundColor)
+ private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, string backgroundColor)
{
var filename = originalPath;
@@ -306,7 +299,6 @@ namespace Emby.Drawing
filename += "quality=" + quality;
filename += "datemodified=" + dateModified.Ticks;
- filename += "length=" + length;
filename += "f=" + format;
@@ -492,17 +484,16 @@ namespace Emby.Drawing
var originalImagePath = image.Path;
var dateModified = image.DateModified;
var imageType = image.Type;
- var length = image.Length;
// Optimization
if (imageEnhancers.Count == 0)
{
- return (originalImagePath + dateModified.Ticks + string.Empty + length).GetMD5().ToString("N");
+ return (originalImagePath + dateModified.Ticks).GetMD5().ToString("N");
}
// Cache name is created with supported enhancers combined with the last config change so we pick up new config changes
var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList();
- cacheKeys.Add(originalImagePath + dateModified.Ticks + string.Empty + length);
+ cacheKeys.Add(originalImagePath + dateModified.Ticks);
return string.Join("|", cacheKeys.ToArray()).GetMD5().ToString("N");
}
@@ -525,7 +516,7 @@ namespace Emby.Drawing
return result.Item1;
}
- private async Task> GetEnhancedImage(ItemImageInfo image,
+ private async Task> GetEnhancedImage(ItemImageInfo image,
IHasImages item,
int imageIndex,
List enhancers)
@@ -533,7 +524,6 @@ namespace Emby.Drawing
var originalImagePath = image.Path;
var dateModified = image.DateModified;
var imageType = image.Type;
- var length = image.Length;
try
{
@@ -553,7 +543,7 @@ namespace Emby.Drawing
_logger.Error("Error enhancing image", ex);
}
- return new Tuple(originalImagePath, dateModified, length);
+ return new Tuple(originalImagePath, dateModified);
}
///
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index e91dd1a9b0..55aa778e2f 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;
}
@@ -114,7 +119,7 @@ namespace MediaBrowser.Api
{
var jobCount = _activeTranscodingJobs.Count;
- Parallel.ForEach(_activeTranscodingJobs.ToList(), j => KillTranscodingJob(j, path => true));
+ Parallel.ForEach(_activeTranscodingJobs.ToList(), j => KillTranscodingJob(j, false, path => true));
// Try to allow for some time to kill the ffmpeg processes and delete the partial stream files
if (jobCount > 0)
@@ -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)
{
@@ -331,7 +339,6 @@ namespace MediaBrowser.Api
return;
}
- // TODO: Lower this hls timeout
var timerDuration = job.Type == TranscodingJobType.Progressive ?
1000 :
1800000;
@@ -339,17 +346,32 @@ namespace MediaBrowser.Api
// We can really reduce the timeout for apps that are using the newer api
if (!string.IsNullOrWhiteSpace(job.PlaySessionId) && job.Type != TranscodingJobType.Progressive)
{
- timerDuration = 20000;
+ timerDuration = 60000;
}
+ job.PingTimeout = timerDuration;
+ job.LastPingDate = DateTime.UtcNow;
+
// Don't start the timer for playback checkins with progressive streaming
if (job.Type != TranscodingJobType.Progressive || !isProgressCheckIn)
{
- job.StartKillTimer(timerDuration, OnTranscodeKillTimerStopped);
+ job.StartKillTimer(OnTranscodeKillTimerStopped);
}
else
{
- job.ChangeKillTimerIfStarted(timerDuration);
+ 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);
+ }
}
}
@@ -361,9 +383,20 @@ namespace MediaBrowser.Api
{
var job = (TranscodingJob)state;
+ if (!job.HasExited && job.Type != TranscodingJobType.Progressive)
+ {
+ var timeSinceLastPing = (DateTime.UtcNow - job.LastPingDate).TotalMilliseconds;
+
+ if (timeSinceLastPing < job.PingTimeout)
+ {
+ job.StartKillTimer(OnTranscodeKillTimerStopped, job.PingTimeout);
+ return;
+ }
+ }
+
Logger.Debug("Transcoding kill timer stopped for JobId {0} PlaySessionId {1}. Killing transcoding", job.Id, job.PlaySessionId);
- KillTranscodingJob(job, path => true);
+ KillTranscodingJob(job, true, path => true);
}
///
@@ -411,7 +444,7 @@ namespace MediaBrowser.Api
foreach (var job in jobs)
{
- KillTranscodingJob(job, deleteFiles);
+ KillTranscodingJob(job, false, deleteFiles);
}
}
@@ -419,8 +452,9 @@ namespace MediaBrowser.Api
/// Kills the transcoding job.
///
/// The job.
+ /// if set to true [close live stream].
/// The delete.
- private void KillTranscodingJob(TranscodingJob job, Func delete)
+ private async void KillTranscodingJob(TranscodingJob job, bool closeLiveStream, Func delete)
{
job.DisposeKillTimer();
@@ -470,6 +504,18 @@ namespace MediaBrowser.Api
{
DeletePartialStreamFiles(job.Path, job.Type, 0, 1500);
}
+
+ if (closeLiveStream && !string.IsNullOrWhiteSpace(job.LiveStreamId))
+ {
+ try
+ {
+ await _mediaSourceManager.CloseLiveStream(job.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error closing live stream for {0}", ex, job.Path);
+ }
+ }
}
private async void DeletePartialStreamFiles(string path, TranscodingJobType jobType, int retryCount, int delayMs)
@@ -578,6 +624,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.
@@ -627,6 +678,9 @@ namespace MediaBrowser.Api
private readonly object _timerLock = new object();
+ public DateTime LastPingDate { get; set; }
+ public int PingTimeout { get; set; }
+
public TranscodingJob(ILogger logger)
{
Logger = logger;
@@ -655,7 +709,12 @@ namespace MediaBrowser.Api
}
}
- public void StartKillTimer(int intervalMs, TimerCallback callback)
+ public void StartKillTimer(TimerCallback callback)
+ {
+ StartKillTimer(callback, PingTimeout);
+ }
+
+ public void StartKillTimer(TimerCallback callback, int intervalMs)
{
CheckHasExited();
@@ -674,7 +733,7 @@ namespace MediaBrowser.Api
}
}
- public void ChangeKillTimerIfStarted(int intervalMs)
+ public void ChangeKillTimerIfStarted()
{
CheckHasExited();
@@ -682,6 +741,8 @@ namespace MediaBrowser.Api
{
if (KillTimer != null)
{
+ var intervalMs = PingTimeout;
+
Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
KillTimer.Change(intervalMs, Timeout.Infinite);
}
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 520c3b9dc7..923af3816a 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.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 1b11f1f337..a55f54d3c8 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -543,7 +543,9 @@ namespace MediaBrowser.Api.Playback.Hls
return false;
}
- return state.VideoRequest.VideoBitRate.HasValue;
+ // Having problems in android
+ return false;
+ //return state.VideoRequest.VideoBitRate.HasValue;
}
private void AppendPlaylist(StringBuilder builder, string url, int bitrate, string subtitleGroup)
diff --git a/MediaBrowser.Api/UserLibrary/UserViewsService.cs b/MediaBrowser.Api/UserLibrary/UserViewsService.cs
index 9b00b42f76..43d8dbc169 100644
--- a/MediaBrowser.Api/UserLibrary/UserViewsService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserViewsService.cs
@@ -88,7 +88,7 @@ namespace MediaBrowser.Api.UserLibrary
var views = user.RootFolder
.GetChildren(user, true)
- .OfType()
+ .OfType()
.Where(i => IsEligibleForSpecialView(i))
.ToList();
@@ -105,9 +105,9 @@ namespace MediaBrowser.Api.UserLibrary
return ToOptimizedResult(list);
}
- private bool IsEligibleForSpecialView(CollectionFolder view)
+ private bool IsEligibleForSpecialView(ICollectionFolder view)
{
- var types = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Games, CollectionType.Music };
+ var types = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Games, CollectionType.Music, CollectionType.Photos };
return types.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
}
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 73ede8179b..22efd3fba7 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -1512,7 +1512,6 @@ namespace MediaBrowser.Controller.Entities
image.Path = file.FullName;
image.DateModified = imageInfo.DateModified;
- image.Length = imageInfo.Length;
}
}
@@ -1622,14 +1621,11 @@ namespace MediaBrowser.Controller.Entities
return null;
}
- var fileInfo = new FileInfo(path);
-
return new ItemImageInfo
{
Path = path,
- DateModified = FileSystem.GetLastWriteTimeUtc(fileInfo),
- Type = imageType,
- Length = fileInfo.Length
+ DateModified = FileSystem.GetLastWriteTimeUtc(path),
+ Type = imageType
};
}
@@ -1690,7 +1686,6 @@ namespace MediaBrowser.Controller.Entities
else
{
existing.DateModified = FileSystem.GetLastWriteTimeUtc(newImage);
- existing.Length = ((FileInfo)newImage).Length;
}
}
@@ -1716,8 +1711,7 @@ namespace MediaBrowser.Controller.Entities
{
Path = file.FullName,
Type = type,
- DateModified = FileSystem.GetLastWriteTimeUtc(file),
- Length = ((FileInfo)file).Length
+ DateModified = FileSystem.GetLastWriteTimeUtc(file)
};
}
@@ -1756,15 +1750,9 @@ namespace MediaBrowser.Controller.Entities
FileSystem.SwapFiles(path1, path2);
- var file1 = new FileInfo(info1.Path);
- var file2 = new FileInfo(info2.Path);
-
// Refresh these values
- info1.DateModified = FileSystem.GetLastWriteTimeUtc(file1);
- info2.DateModified = FileSystem.GetLastWriteTimeUtc(file2);
-
- info1.Length = file1.Length;
- info2.Length = file2.Length;
+ info1.DateModified = FileSystem.GetLastWriteTimeUtc(info1.Path);
+ info2.DateModified = FileSystem.GetLastWriteTimeUtc(info2.Path);
return UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
}
diff --git a/MediaBrowser.Controller/Entities/ItemImageInfo.cs b/MediaBrowser.Controller/Entities/ItemImageInfo.cs
index 1122de4032..b36b818ffe 100644
--- a/MediaBrowser.Controller/Entities/ItemImageInfo.cs
+++ b/MediaBrowser.Controller/Entities/ItemImageInfo.cs
@@ -11,12 +11,6 @@ namespace MediaBrowser.Controller.Entities
/// The path.
public string Path { get; set; }
- ///
- /// Gets or sets the length.
- ///
- /// The length.
- public long Length { get; set; }
-
///
/// Gets or sets the type.
///
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index a6d123fe86..1672bda926 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -121,7 +121,6 @@ namespace MediaBrowser.Controller.Entities
}
case CollectionType.Books:
- case CollectionType.Photos:
case CollectionType.HomeVideos:
case CollectionType.MusicVideos:
return GetResult(queryParent.GetChildren(user, true), queryParent, query);
@@ -138,6 +137,9 @@ namespace MediaBrowser.Controller.Entities
case CollectionType.BoxSets:
return await GetBoxsetView(queryParent, user, query).ConfigureAwait(false);
+ case CollectionType.Photos:
+ return await GetPhotosView(queryParent, user, query).ConfigureAwait(false);
+
case CollectionType.TvShows:
return await GetTvView(queryParent, user, query).ConfigureAwait(false);
@@ -247,16 +249,16 @@ namespace MediaBrowser.Controller.Entities
return GetFavoriteSongs(queryParent, user, query);
default:
- {
- if (queryParent is UserView)
- {
- return GetResult(GetMediaFolders(user).SelectMany(i => i.GetChildren(user, true)), queryParent, query);
- }
- else
{
- return GetResult(queryParent.GetChildren(user, true), queryParent, query);
+ if (queryParent is UserView)
+ {
+ return GetResult(GetMediaFolders(user).SelectMany(i => i.GetChildren(user, true)), queryParent, query);
+ }
+ else
+ {
+ return GetResult(queryParent.GetChildren(user, true), queryParent, query);
+ }
}
- }
}
}
@@ -645,6 +647,19 @@ namespace MediaBrowser.Controller.Entities
}), parent, query);
}
+ private async Task> GetPhotosView(Folder queryParent, User user, InternalItemsQuery query)
+ {
+ if (query.Recursive)
+ {
+ var mediaTypes = new[] { MediaType.Video, MediaType.Photo };
+ var items = GetRecursiveChildren(queryParent, user, new[] { CollectionType.Photos, string.Empty }, i => (i is PhotoAlbum || mediaTypes.Contains(i.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) && FilterItem(i, query));
+
+ return PostFilterAndSort(items, queryParent, null, query);
+ }
+
+ return GetResult(queryParent.GetChildren(user, true), queryParent, query);
+ }
+
private async Task> GetTvView(Folder parent, User user, InternalItemsQuery query)
{
if (query.Recursive)
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index d16589f07f..c009121154 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.Controller/LiveTv/LiveTvTunerInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs
index cdf2f0ef52..46cf4dd98c 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs
@@ -1,5 +1,5 @@
-using System.Collections.Generic;
-using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Model.LiveTv;
+using System.Collections.Generic;
namespace MediaBrowser.Controller.LiveTv
{
@@ -23,6 +23,12 @@ namespace MediaBrowser.Controller.LiveTv
/// The identifier.
public string Id { get; set; }
+ ///
+ /// Gets or sets the URL.
+ ///
+ /// The URL.
+ public string Url { get; set; }
+
///
/// Gets or sets the status.
///
diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
index 1aa8d7f1f1..87378fd5a7 100644
--- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
+++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
@@ -488,6 +488,9 @@
Dto\ItemIndex.cs
+
+ Dto\ItemLayout.cs
+
Dto\MediaSourceInfo.cs
diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
index 5d4633f507..61057e5bbb 100644
--- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
+++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
@@ -453,6 +453,9 @@
Dto\ItemIndex.cs
+
+ Dto\ItemLayout.cs
+
Dto\MediaSourceInfo.cs
diff --git a/MediaBrowser.Model/ApiClient/ServerCredentials.cs b/MediaBrowser.Model/ApiClient/ServerCredentials.cs
index 6ba2a80c87..b5a1a7b499 100644
--- a/MediaBrowser.Model/ApiClient/ServerCredentials.cs
+++ b/MediaBrowser.Model/ApiClient/ServerCredentials.cs
@@ -98,7 +98,7 @@ namespace MediaBrowser.Model.ApiClient
{
var index = 0;
- foreach (var server in servers)
+ foreach (ServerInfo server in servers)
{
if (StringHelper.EqualsIgnoreCase(id, server.Id))
{
@@ -110,5 +110,18 @@ namespace MediaBrowser.Model.ApiClient
return -1;
}
+
+ public ServerInfo GetServer(string id)
+ {
+ foreach (ServerInfo server in Servers)
+ {
+ if (StringHelper.EqualsIgnoreCase(id, server.Id))
+ {
+ return server;
+ }
+ }
+
+ return null;
+ }
}
}
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index 72f9174388..ac9bd6b08f 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 02f77899f6..30da8bb4c9 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -277,7 +277,7 @@ namespace MediaBrowser.Model.Dlna
// The profile describes what the device supports
// If device requirements are satisfied then allow both direct stream and direct play
- if (IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options)))
+ if (item.SupportsDirectPlay && IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options)))
{
playMethods.Add(PlayMethod.DirectPlay);
}
@@ -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);
}
}
@@ -640,7 +638,7 @@ namespace MediaBrowser.Model.Dlna
}
}
- if (isEligibleForDirectPlay)
+ if (isEligibleForDirectPlay && mediaSource.SupportsDirectPlay)
{
if (mediaSource.Protocol == MediaProtocol.Http)
{
@@ -659,12 +657,9 @@ namespace MediaBrowser.Model.Dlna
}
}
- if (isEligibleForDirectStream)
+ if (isEligibleForDirectStream && mediaSource.SupportsDirectStream)
{
- if (mediaSource.SupportsDirectStream)
- {
- return PlayMethod.DirectStream;
- }
+ return PlayMethod.DirectStream;
}
return null;
diff --git a/MediaBrowser.Model/Dto/ItemLayout.cs b/MediaBrowser.Model/Dto/ItemLayout.cs
new file mode 100644
index 0000000000..c85818390d
--- /dev/null
+++ b/MediaBrowser.Model/Dto/ItemLayout.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Model.Dto
+{
+ public static class ItemLayout
+ {
+ public static double? GetDisplayAspectRatio(BaseItemDto item)
+ {
+ List items = new List();
+ items.Add(item);
+ return GetDisplayAspectRatio(items);
+ }
+
+ public static double? GetDisplayAspectRatio(List items)
+ {
+ List values = new List();
+
+ foreach (BaseItemDto item in items)
+ {
+ if (item.PrimaryImageAspectRatio.HasValue)
+ {
+ values.Add(item.PrimaryImageAspectRatio.Value);
+ }
+ }
+
+ if (values.Count == 0)
+ {
+ return null;
+ }
+
+ values.Sort();
+
+ double halfDouble = values.Count;
+ halfDouble /= 2;
+ int half = Convert.ToInt32(Math.Floor(halfDouble));
+
+ double result;
+
+ if (values.Count % 2 > 0)
+ result = values[half];
+ else
+ result = (values[half - 1] + values[half]) / 2.0;
+
+ // If really close to 2:3 (poster image), just return 2:3
+ if (Math.Abs(0.66666666667 - result) <= .15)
+ {
+ return 0.66666666667;
+ }
+
+ // If really close to 16:9 (episode image), just return 16:9
+ if (Math.Abs(1.777777778 - result) <= .2)
+ {
+ return 1.777777778;
+ }
+
+ // If really close to 1 (square image), just return 1
+ if (Math.Abs(1 - result) <= .15)
+ {
+ return 1.0;
+ }
+
+ // If really close to 4:3 (poster image), just return 2:3
+ if (Math.Abs(1.33333333333 - result) <= .15)
+ {
+ return 1.33333333333;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/MediaBrowser.Model/LiveTv/LiveTvTunerInfoDto.cs b/MediaBrowser.Model/LiveTv/LiveTvTunerInfoDto.cs
index 28e8c158ae..fcb19427ba 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvTunerInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvTunerInfoDto.cs
@@ -22,6 +22,12 @@ namespace MediaBrowser.Model.LiveTv
/// The identifier.
public string Id { get; set; }
+ ///
+ /// Gets or sets the URL.
+ ///
+ /// The URL.
+ public string Url { get; set; }
+
///
/// Gets or sets the status.
///
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index eb36712c2f..af0cbb1665 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -139,6 +139,7 @@
+
diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
index a3b5691500..fc47b0259a 100644
--- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs
+++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
@@ -384,7 +384,6 @@ namespace MediaBrowser.Providers.Manager
else
{
currentImage.DateModified = _fileSystem.GetLastWriteTimeUtc(image.FileInfo);
- currentImage.Length = ((FileInfo) image.FileInfo).Length;
}
}
else
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index 3cfecdeafb..334e148506 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -482,6 +482,11 @@ namespace MediaBrowser.Providers.Manager
protected virtual bool IsFullLocalMetadata(TItemType item)
{
+ if (string.IsNullOrWhiteSpace(item.Name))
+ {
+ return false;
+ }
+
return true;
}
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index f4d8ddb1a8..b53e313168 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.Providers/Movies/MovieMetadataService.cs b/MediaBrowser.Providers/Movies/MovieMetadataService.cs
index 172ae6814c..f886deb007 100644
--- a/MediaBrowser.Providers/Movies/MovieMetadataService.cs
+++ b/MediaBrowser.Providers/Movies/MovieMetadataService.cs
@@ -36,10 +36,6 @@ namespace MediaBrowser.Providers.Movies
protected override bool IsFullLocalMetadata(Movie item)
{
- if (string.IsNullOrWhiteSpace(item.Name))
- {
- return false;
- }
if (string.IsNullOrWhiteSpace(item.Overview))
{
return false;
diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs
index a5959f0b75..b0cd7382a1 100644
--- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs
+++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs
@@ -77,10 +77,6 @@ namespace MediaBrowser.Providers.TV
protected override bool IsFullLocalMetadata(Series item)
{
- if (string.IsNullOrWhiteSpace(item.Name))
- {
- return false;
- }
if (string.IsNullOrWhiteSpace(item.Overview))
{
return false;
diff --git a/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs
index de1a4c6f93..a31cc1e0c1 100644
--- a/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs
@@ -243,7 +243,15 @@ namespace MediaBrowser.Providers.TV
await SanitizeXmlFile(file).ConfigureAwait(false);
}
- await ExtractEpisodes(seriesDataPath, Path.Combine(seriesDataPath, saveAsMetadataLanguage + ".xml"), lastTvDbUpdateTime).ConfigureAwait(false);
+ var downloadLangaugeXmlFile = Path.Combine(seriesDataPath, preferredMetadataLanguage + ".xml");
+ var saveAsLanguageXmlFile = Path.Combine(seriesDataPath, saveAsMetadataLanguage + ".xml");
+
+ if (!string.Equals(downloadLangaugeXmlFile, saveAsLanguageXmlFile, StringComparison.OrdinalIgnoreCase))
+ {
+ File.Copy(downloadLangaugeXmlFile, saveAsLanguageXmlFile, true);
+ }
+
+ await ExtractEpisodes(seriesDataPath, downloadLangaugeXmlFile, lastTvDbUpdateTime).ConfigureAwait(false);
}
public TvdbOptions GetTvDbOptions()
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
index 190b8fcf4c..f44b7b5a84 100644
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
@@ -828,14 +828,11 @@ namespace MediaBrowser.Server.Implementations.Dto
if (!string.IsNullOrEmpty(chapterInfo.ImagePath))
{
- var file = new FileInfo(chapterInfo.ImagePath);
-
dto.ImageTag = GetImageCacheTag(item, new ItemImageInfo
{
Path = chapterInfo.ImagePath,
Type = ImageType.Chapter,
- DateModified = _fileSystem.GetLastWriteTimeUtc(file),
- Length = file.Length
+ DateModified = _fileSystem.GetLastWriteTimeUtc(chapterInfo.ImagePath)
});
}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
index 966e0a3e48..3fa0df7608 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
+++ b/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
@@ -1,5 +1,4 @@
-using System.Linq;
-using MediaBrowser.Controller;
+using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging;
@@ -139,55 +138,24 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
// On some systems the device discovered event seems to fire repeatedly
// This check will help ensure we're not trying to port map the same device over and over
- List currentMappings = null;
-
- try
- {
- currentMappings = device.GetAllMappings().ToList();
- }
- catch (NotSupportedException)
- {
- }
-
var address = device.LocalAddress.ToString();
if (!_createdRules.Contains(address))
{
_createdRules.Add(address);
- CreatePortMap(device, currentMappings, _appHost.HttpPort, _config.Configuration.PublicPort);
- CreatePortMap(device, currentMappings, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort);
+ CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort);
+ CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort);
}
}
- private void CreatePortMap(INatDevice device, List currentMappings, int privatePort, int publicPort)
+ private void CreatePortMap(INatDevice device, int privatePort, int publicPort)
{
- var hasMapping = false;
-
- if (currentMappings != null)
- {
- hasMapping = currentMappings.Any(i => i.PublicPort == publicPort && i.PrivatePort == privatePort);
- }
- else
+ _logger.Debug("Creating port map on port {0}", privatePort);
+ device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort)
{
- try
- {
- var mapping = device.GetSpecificMapping(Protocol.Tcp, publicPort);
- hasMapping = mapping != null;
- }
- catch (NotSupportedException)
- {
- }
- }
-
- if (!hasMapping)
- {
- _logger.Debug("Creating port map on port {0}", privatePort);
- device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort)
- {
- Description = _appHost.Name
- });
- }
+ Description = _appHost.Name
+ });
}
// As I said before, this method will be never invoked. You can remove it.
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index 313985d9df..dcdf2aa7dc 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"));
@@ -1723,7 +1731,7 @@ namespace MediaBrowser.Server.Implementations.Library
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
}
- var refresh = isNew || (DateTime.UtcNow - item.DateLastSaved).TotalHours >= 12;
+ var refresh = isNew || (DateTime.UtcNow - item.DateLastSaved).TotalHours >= 24;
if (refresh)
{
diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
index a21b19e04c..5cff9046a3 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,16 +202,27 @@ 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);
+
if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase)))
{
- var name = parents[0].Name;
+ if (!string.IsNullOrWhiteSpace(parents[0].Name))
+ {
+ name = parents[0].Name;
+ }
+
var parentId = parents[0].Id;
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);
@@ -224,18 +237,14 @@ namespace MediaBrowser.Server.Implementations.Library
return await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
}
- else
- {
- var name = _localizationManager.GetLocalizedString("ViewType" + viewType);
- 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)
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
index 61017ffb35..00c15fdfcc 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -293,7 +293,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
ProgramName = info.ProgramName,
SourceType = info.SourceType,
Status = info.Status,
- ChannelName = channelName
+ ChannelName = channelName,
+ Url = info.Url
};
if (!string.IsNullOrEmpty(info.ChannelId))
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
index 84b4053a19..38c93a6964 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
@@ -122,7 +122,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken)
{
- var inputPaths = new[] { mediaSource.Path };
+ var originalRuntime = mediaSource.RunTimeTicks;
var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
{
@@ -131,8 +131,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
ExtractChapters = false
- }, cancellationToken)
- .ConfigureAwait(false);
+ }, cancellationToken).ConfigureAwait(false);
mediaSource.Bitrate = info.Bitrate;
mediaSource.Container = info.Container;
@@ -146,6 +145,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
mediaSource.DefaultSubtitleStreamIndex = null;
+ // Null this out so that it will be treated like a live stream
+ if (!originalRuntime.HasValue)
+ {
+ mediaSource.RunTimeTicks = null;
+ }
+
var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Audio);
if (audioStream == null || audioStream.Index == -1)
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json
index 1affc43bf8..f464dad4ba 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/server.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json
@@ -1433,5 +1433,7 @@
"ToAccessPreferencesHelp": "To access your preferences later, click your user icon in the top right header and select My Preferences.",
"HeaderViewStyles": "View Styles",
"LabelSelectViewStyles": "Enable rich presentations for:",
- "LabelSelectViewStylesHelp": "If enabled, views will be built with metadata to offer categories such as Suggestions, Latest, Genres, and more. If disabled, they'll be displayed with simple folders."
+ "LabelSelectViewStylesHelp": "If enabled, views will be built with metadata to offer categories such as Suggestions, Latest, Genres, and more. If disabled, they'll be displayed with simple folders.",
+ "TabPhotos": "Photos",
+ "TabVideos": "Videos"
}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
index 6f0310e3c3..93a8fb86a5 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
+++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
@@ -727,10 +727,14 @@ namespace MediaBrowser.Server.Implementations.Sync
}
});
- return jobItemResult.Items
+ var readyItems = jobItemResult.Items
.Select(GetJobItemInfo)
.Where(i => i != null)
.ToList();
+
+ _logger.Debug("Returning {0} ready sync items for targetId {1}", readyItems.Count, targetId);
+
+ return readyItems;
}
public async Task SyncData(SyncDataRequest request)
diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
index 90af13b579..030d687918 100644
--- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs
+++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
@@ -470,6 +470,7 @@ namespace MediaBrowser.WebDashboard.Api
"notificationlist.js",
"notificationsetting.js",
"notificationsettings.js",
+ "photos.js",
"playlists.js",
"playlistedit.js",
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index 7abe2ded98..5c4aab1e4f 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -132,6 +132,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
@@ -147,6 +150,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec
index f3f5bbd8c4..269fc720a0 100644
--- a/Nuget/MediaBrowser.Common.Internal.nuspec
+++ b/Nuget/MediaBrowser.Common.Internal.nuspec
@@ -2,7 +2,7 @@
MediaBrowser.Common.Internal
- 3.0.620
+ 3.0.621
MediaBrowser.Common.Internal
Luke
ebr,Luke,scottisafool
@@ -12,7 +12,7 @@
Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.
Copyright © Emby 2013
-
+
diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec
index 9804371d7d..e2577369a0 100644
--- a/Nuget/MediaBrowser.Common.nuspec
+++ b/Nuget/MediaBrowser.Common.nuspec
@@ -2,7 +2,7 @@
MediaBrowser.Common
- 3.0.620
+ 3.0.621
MediaBrowser.Common
Emby Team
ebr,Luke,scottisafool
diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec
index 8a47ff07a8..cb467b5e5b 100644
--- a/Nuget/MediaBrowser.Model.Signed.nuspec
+++ b/Nuget/MediaBrowser.Model.Signed.nuspec
@@ -2,7 +2,7 @@
MediaBrowser.Model.Signed
- 3.0.620
+ 3.0.621
MediaBrowser.Model - Signed Edition
Emby Team
ebr,Luke,scottisafool
diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec
index fbe9e66c6f..a576cab49f 100644
--- a/Nuget/MediaBrowser.Server.Core.nuspec
+++ b/Nuget/MediaBrowser.Server.Core.nuspec
@@ -2,7 +2,7 @@
MediaBrowser.Server.Core
- 3.0.620
+ 3.0.621
Media Browser.Server.Core
Emby Team
ebr,Luke,scottisafool
@@ -12,7 +12,7 @@
Contains core components required to build plugins for Emby Server.
Copyright © Emby 2013
-
+
diff --git a/SharedVersion.cs b/SharedVersion.cs
index 5d75f7887f..49945f62ac 100644
--- a/SharedVersion.cs
+++ b/SharedVersion.cs
@@ -1,4 +1,4 @@
using System.Reflection;
//[assembly: AssemblyVersion("3.0.*")]
-[assembly: AssemblyVersion("3.0.5582.4")]
+[assembly: AssemblyVersion("3.0.5588.0")]