diff options
39 files changed, 380 insertions, 200 deletions
diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index 6d600c646..55c6f6455 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 /// <summary> /// Crops whitespace from an image, caches the result, and returns the cached path /// </summary> - private async Task<Tuple<string, DateTime, long>> GetWhitespaceCroppedImage(string originalImagePath, DateTime dateModified, long length) + private async Task<Tuple<string, DateTime>> 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<string, DateTime, long>(originalImagePath, dateModified, length); + return new Tuple<string, DateTime>(originalImagePath, dateModified); } finally { @@ -280,11 +275,9 @@ namespace Emby.Drawing return GetResult(croppedImagePath); } - private Tuple<string, DateTime, long> GetResult(string path) + private Tuple<string, DateTime> GetResult(string path) { - var file = new FileInfo(path); - - return new Tuple<string, DateTime, long>(path, _fileSystem.GetLastWriteTimeUtc(file), file.Length); + return new Tuple<string, DateTime>(path, _fileSystem.GetLastWriteTimeUtc(path)); } /// <summary> @@ -295,7 +288,7 @@ namespace Emby.Drawing /// <summary> /// Gets the cache file path based on a set of parameters /// </summary> - 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<Tuple<string, DateTime, long>> GetEnhancedImage(ItemImageInfo image, + private async Task<Tuple<string, DateTime>> GetEnhancedImage(ItemImageInfo image, IHasImages item, int imageIndex, List<IImageEnhancer> 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<string, DateTime, long>(originalImagePath, dateModified, length); + return new Tuple<string, DateTime>(originalImagePath, dateModified); } /// <summary> diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index e91dd1a9b..55aa778e2 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 /// <param name="logger">The logger.</param> /// <param name="sessionManager">The session manager.</param> /// <param name="config">The configuration.</param> - public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config, IFileSystem fileSystem) + /// <param name="fileSystem">The file system.</param> + /// <param name="mediaSourceManager">The media source manager.</param> + 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 /// </summary> /// <param name="path">The path.</param> /// <param name="playSessionId">The play session identifier.</param> + /// <param name="liveStreamId">The live stream identifier.</param> /// <param name="transcodingJobId">The transcoding job identifier.</param> /// <param name="type">The type.</param> /// <param name="process">The process.</param> @@ -142,6 +148,7 @@ namespace MediaBrowser.Api /// <returns>TranscodingJob.</returns> 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); } /// <summary> @@ -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. /// </summary> /// <param name="job">The job.</param> + /// <param name="closeLiveStream">if set to <c>true</c> [close live stream].</param> /// <param name="delete">The delete.</param> - private void KillTranscodingJob(TranscodingJob job, Func<string, bool> delete) + private async void KillTranscodingJob(TranscodingJob job, bool closeLiveStream, Func<string, bool> 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 /// <value>The play session identifier.</value> public string PlaySessionId { get; set; } /// <summary> + /// Gets or sets the live stream identifier. + /// </summary> + /// <value>The live stream identifier.</value> + public string LiveStreamId { get; set; } + /// <summary> /// Gets or sets the path. /// </summary> /// <value>The path.</value> @@ -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 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.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 1b11f1f33..a55f54d3c 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 9b00b42f7..43d8dbc16 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<CollectionFolder>() + .OfType<ICollectionFolder>() .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 73ede8179..22efd3fba 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 1122de403..b36b818ff 100644 --- a/MediaBrowser.Controller/Entities/ItemImageInfo.cs +++ b/MediaBrowser.Controller/Entities/ItemImageInfo.cs @@ -12,12 +12,6 @@ namespace MediaBrowser.Controller.Entities public string Path { get; set; } /// <summary> - /// Gets or sets the length. - /// </summary> - /// <value>The length.</value> - public long Length { get; set; } - - /// <summary> /// Gets or sets the type. /// </summary> /// <value>The type.</value> diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index a6d123fe8..1672bda92 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<QueryResult<BaseItem>> 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<QueryResult<BaseItem>> 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 d16589f07..c00912115 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -309,6 +309,7 @@ namespace MediaBrowser.Controller.Library /// <param name="parentId">The parent identifier.</param> /// <param name="viewType">Type of the view.</param> /// <param name="sortName">Name of the sort.</param> + /// <param name="uniqueId">The unique identifier.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task<UserView>.</returns> Task<UserView> GetNamedView(User user, @@ -316,6 +317,7 @@ namespace MediaBrowser.Controller.Library string parentId, string viewType, string sortName, + string uniqueId, CancellationToken cancellationToken); /// <summary> diff --git a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs index cdf2f0ef5..46cf4dd98 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 { @@ -24,6 +24,12 @@ namespace MediaBrowser.Controller.LiveTv public string Id { get; set; } /// <summary> + /// Gets or sets the URL. + /// </summary> + /// <value>The URL.</value> + public string Url { get; set; } + + /// <summary> /// Gets or sets the status. /// </summary> /// <value>The status.</value> diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index 1aa8d7f1f..87378fd5a 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -488,6 +488,9 @@ <Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs"> <Link>Dto\ItemIndex.cs</Link> </Compile> + <Compile Include="..\MediaBrowser.Model\Dto\ItemLayout.cs"> + <Link>Dto\ItemLayout.cs</Link> + </Compile> <Compile Include="..\MediaBrowser.Model\Dto\MediaSourceInfo.cs"> <Link>Dto\MediaSourceInfo.cs</Link> </Compile> diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 5d4633f50..61057e5bb 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -453,6 +453,9 @@ <Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs"> <Link>Dto\ItemIndex.cs</Link> </Compile> + <Compile Include="..\MediaBrowser.Model\Dto\ItemLayout.cs"> + <Link>Dto\ItemLayout.cs</Link> + </Compile> <Compile Include="..\MediaBrowser.Model\Dto\MediaSourceInfo.cs"> <Link>Dto\MediaSourceInfo.cs</Link> </Compile> diff --git a/MediaBrowser.Model/ApiClient/ServerCredentials.cs b/MediaBrowser.Model/ApiClient/ServerCredentials.cs index 6ba2a80c8..b5a1a7b49 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 72f917438..ac9bd6b08 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -49,7 +49,7 @@ namespace MediaBrowser.Model.Configuration /// </summary> /// <value><c>true</c> if [enable user specific user views]; otherwise, <c>false</c>.</value> public bool EnableUserSpecificUserViews { get; set; } - + /// <summary> /// Gets or sets the value pointing to the file system where the ssl certiifcate is located.. /// </summary> diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 02f77899f..30da8bb4c 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 000000000..c85818390 --- /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<BaseItemDto> items = new List<BaseItemDto>(); + items.Add(item); + return GetDisplayAspectRatio(items); + } + + public static double? GetDisplayAspectRatio(List<BaseItemDto> items) + { + List<double> values = new List<double>(); + + 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 28e8c158a..fcb19427b 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvTunerInfoDto.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvTunerInfoDto.cs @@ -23,6 +23,12 @@ namespace MediaBrowser.Model.LiveTv public string Id { get; set; } /// <summary> + /// Gets or sets the URL. + /// </summary> + /// <value>The URL.</value> + public string Url { get; set; } + + /// <summary> /// Gets or sets the status. /// </summary> /// <value>The status.</value> diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index eb36712c2..af0cbb166 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -139,6 +139,7 @@ <Compile Include="Drawing\ImageOrientation.cs" /> <Compile Include="Dto\IHasServerId.cs" /> <Compile Include="Dto\IHasSyncInfo.cs" /> + <Compile Include="Dto\ItemLayout.cs" /> <Compile Include="Dto\MetadataEditorInfo.cs" /> <Compile Include="Dto\NameIdPair.cs" /> <Compile Include="Dto\NameValuePair.cs" /> diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index a3b569150..fc47b0259 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 3cfecdeaf..334e14850 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 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.Providers/Movies/MovieMetadataService.cs b/MediaBrowser.Providers/Movies/MovieMetadataService.cs index 172ae6814..f886deb00 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 a5959f0b7..b0cd7382a 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 de1a4c6f9..a31cc1e0c 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 190b8fcf4..f44b7b5a8 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 966e0a3e4..3fa0df760 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<Mapping> 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<Mapping> 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 313985d9d..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<UserView> 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 a21b19e04..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<ICollectionFolder>(), CollectionType.Folders, "zz_" + CollectionType.Folders, user, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(new List<ICollectionFolder>(), 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<ICollectionFolder>(), CollectionType.LiveTv, string.Empty, user, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(new List<ICollectionFolder>(), list, CollectionType.LiveTv, string.Empty, user, cancellationToken).ConfigureAwait(false)); } } @@ -190,7 +190,9 @@ namespace MediaBrowser.Server.Implementations.Library public Task<UserView> 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<UserView> 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<UserView> GetUserView(List<ICollectionFolder> parents, string viewType, string sortName, User user, CancellationToken cancellationToken) + public async Task<UserView> GetUserView(List<ICollectionFolder> parents, List<Folder> 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<UserView>().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<UserView> 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<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request) diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs index 61017ffb3..00c15fdfc 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 84b4053a1..38c93a696 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 1affc43bf..f464dad4b 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 6f0310e3c..93a8fb86a 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<SyncDataResponse> SyncData(SyncDataRequest request) diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 90af13b57..030d68791 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 7abe2ded9..5c4aab1e4 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -132,6 +132,9 @@ <Content Include="dashboard-ui\mysyncjob.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\photos.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\scripts\dashboardhosting.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -147,6 +150,9 @@ <Content Include="dashboard-ui\scripts\livetvitems.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\scripts\photos.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\scripts\selectserver.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index f3f5bbd8c..269fc720a 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common.Internal</id> - <version>3.0.620</version> + <version>3.0.621</version> <title>MediaBrowser.Common.Internal</title> <authors>Luke</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description> <copyright>Copyright © Emby 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.620" /> + <dependency id="MediaBrowser.Common" version="3.0.621" /> <dependency id="NLog" version="3.2.0.0" /> <dependency id="SimpleInjector" version="2.7.0" /> </dependencies> diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 9804371d7..e2577369a 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common</id> - <version>3.0.620</version> + <version>3.0.621</version> <title>MediaBrowser.Common</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index 8a47ff07a..cb467b5e5 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Model.Signed</id> - <version>3.0.620</version> + <version>3.0.621</version> <title>MediaBrowser.Model - Signed Edition</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index fbe9e66c6..a576cab49 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <metadata> <id>MediaBrowser.Server.Core</id> - <version>3.0.620</version> + <version>3.0.621</version> <title>Media Browser.Server.Core</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains core components required to build plugins for Emby Server.</description> <copyright>Copyright © Emby 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.620" /> + <dependency id="MediaBrowser.Common" version="3.0.621" /> <dependency id="Interfaces.IO" version="1.0.0.5" /> </dependencies> </metadata> diff --git a/SharedVersion.cs b/SharedVersion.cs index 5d75f7887..49945f62a 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")] |
