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 /// /// 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 c7f470427..f59b1b7df 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -331,7 +331,6 @@ namespace MediaBrowser.Api return; } - // TODO: Lower this hls timeout var timerDuration = job.Type == TranscodingJobType.Progressive ? 1000 : 1800000; @@ -342,14 +341,17 @@ namespace MediaBrowser.Api 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(); } } @@ -361,6 +363,15 @@ namespace MediaBrowser.Api { var job = (TranscodingJob)state; + if (!job.HasExited && job.Type != TranscodingJobType.Progressive) + { + if ((DateTime.UtcNow - job.LastPingDate).TotalMilliseconds < job.PingTimeout) + { + job.StartKillTimer(OnTranscodeKillTimerStopped); + return; + } + } + Logger.Debug("Transcoding kill timer stopped for JobId {0} PlaySessionId {1}. Killing transcoding", job.Id, job.PlaySessionId); KillTranscodingJob(job, path => true); @@ -627,6 +638,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,12 +669,14 @@ namespace MediaBrowser.Api } } - public void StartKillTimer(int intervalMs, TimerCallback callback) + public void StartKillTimer(TimerCallback callback) { CheckHasExited(); lock (_timerLock) { + var intervalMs = PingTimeout; + if (KillTimer == null) { Logger.Debug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); @@ -674,7 +690,7 @@ namespace MediaBrowser.Api } } - public void ChangeKillTimerIfStarted(int intervalMs) + public void ChangeKillTimerIfStarted() { CheckHasExited(); @@ -682,6 +698,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/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() + .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 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 @@ -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 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); + 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); + } } - 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.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 @@ 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 5d4633f50..61057e5bb 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 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/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 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/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 @@ + 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/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 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) + _logger.Debug("Creating port map on port {0}", privatePort); + device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort) { - hasMapping = currentMappings.Any(i => i.PublicPort == publicPort && i.PrivatePort == privatePort); - } - else - { - 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..e99d01d54 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -1723,7 +1723,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..823599fe5 100644 --- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs @@ -202,9 +202,15 @@ namespace MediaBrowser.Server.Implementations.Library public async Task GetUserView(List parents, 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); @@ -226,8 +232,6 @@ namespace MediaBrowser.Server.Implementations.Library } else { - var name = _localizationManager.GetLocalizedString("ViewType" + viewType); - return await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false); } } 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.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 9b97c1e98..e45f52419 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 b0379de5c..5c4aab1e4 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -132,7 +132,7 @@ PreserveNewest - + PreserveNewest @@ -150,7 +150,7 @@ PreserveNewest - + PreserveNewest