diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 66ca8c25d4..a677bc6004 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -488,13 +488,17 @@ namespace MediaBrowser.Api { try { - Logger.Info("Killing ffmpeg process for {0}", job.Path); + Logger.Info("Stopping ffmpeg process with q command for {0}", job.Path); //process.Kill(); process.StandardInput.WriteLine("q"); // Need to wait because killing is asynchronous - process.WaitForExit(5000); + if (!process.WaitForExit(5000)) + { + Logger.Info("Killing ffmpeg process for {0}", job.Path); + process.Kill(); + } } catch (Exception ex) { diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index ebcf8fbeac..d3a4558c88 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -254,6 +254,8 @@ namespace MediaBrowser.Api.LiveTv [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] public bool? EnableImages { get; set; } + public bool EnableTotalRecordCount { get; set; } + [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? ImageTypeLimit { get; set; } @@ -266,12 +268,24 @@ namespace MediaBrowser.Api.LiveTv /// The fields. [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } + + public GetPrograms() + { + EnableTotalRecordCount = true; + } } [Route("/LiveTv/Programs/Recommended", "GET", Summary = "Gets available live tv epgs..")] [Authenticated] public class GetRecommendedPrograms : IReturn>, IHasDtoOptions { + public bool EnableTotalRecordCount { get; set; } + + public GetRecommendedPrograms() + { + EnableTotalRecordCount = true; + } + [ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] public string UserId { get; set; } @@ -662,7 +676,8 @@ namespace MediaBrowser.Api.LiveTv { ChannelIds = (request.ChannelIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToArray(), UserId = request.UserId, - HasAired = request.HasAired + HasAired = request.HasAired, + EnableTotalRecordCount = request.EnableTotalRecordCount }; if (!string.IsNullOrEmpty(request.MinStartDate)) @@ -709,7 +724,8 @@ namespace MediaBrowser.Api.LiveTv HasAired = request.HasAired, IsMovie = request.IsMovie, IsKids = request.IsKids, - IsSports = request.IsSports + IsSports = request.IsSports, + EnableTotalRecordCount = request.EnableTotalRecordCount }; var result = await _liveTvManager.GetRecommendedPrograms(query, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false); diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index 9590971d06..786615cc3e 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -196,7 +196,6 @@ namespace MediaBrowser.Api.Movies var parentIds = new string[] { }; var list = _libraryManager.GetItemList(query, parentIds) - .DistinctBy(i => i.PresentationUniqueKey, StringComparer.OrdinalIgnoreCase) .DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N")) .ToList(); diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs index 6867f6308c..aee1a8d545 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs @@ -12,6 +12,7 @@ namespace MediaBrowser.Api.UserLibrary protected BaseItemsRequest() { EnableImages = true; + EnableTotalRecordCount = true; } /// @@ -104,7 +105,9 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "IsInBoxSet", Description = "Optional filter by items that are in boxsets, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? IsInBoxSet { get; set; } - + + public bool EnableTotalRecordCount { get; set; } + /// /// Skips over a given number of items within the results. Use for paging. /// diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 7d2029cabe..ff937078e6 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -230,7 +230,8 @@ namespace MediaBrowser.Api.UserLibrary ParentId = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId), ParentIndexNumber = request.ParentIndexNumber, AiredDuringSeason = request.AiredDuringSeason, - AlbumArtistStartsWithOrGreater = request.AlbumArtistStartsWithOrGreater + AlbumArtistStartsWithOrGreater = request.AlbumArtistStartsWithOrGreater, + EnableTotalRecordCount = request.EnableTotalRecordCount }; if (!string.IsNullOrWhiteSpace(request.Ids)) diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index fd56a6746a..c34a884ff5 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -40,12 +40,6 @@ namespace MediaBrowser.Controller.Entities.Audio public List AlbumArtists { get; set; } - /// - /// Gets or sets the album. - /// - /// The album. - public string Album { get; set; } - [IgnoreDataMember] public bool IsThemeMedia { diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index 5cb4e8c9d1..615276e837 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -48,6 +48,15 @@ namespace MediaBrowser.Controller.Entities.Audio } } + [IgnoreDataMember] + public override bool SupportsCumulativeRunTimeTicks + { + get + { + return true; + } + } + [IgnoreDataMember] public List AllArtists { diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 6f6f124dbb..6104976615 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -34,6 +34,15 @@ namespace MediaBrowser.Controller.Entities.Audio } } + [IgnoreDataMember] + public override bool SupportsCumulativeRunTimeTicks + { + get + { + return true; + } + } + [IgnoreDataMember] public override bool SupportsAddingToPlaylist { diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 2e968c8803..cd021d2ab5 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -69,6 +69,12 @@ namespace MediaBrowser.Controller.Entities public List ImageInfos { get; set; } + /// + /// Gets or sets the album. + /// + /// The album. + public string Album { get; set; } + /// /// Gets or sets the channel identifier. /// @@ -1175,6 +1181,11 @@ namespace MediaBrowser.Controller.Entities get { return Id.ToString("N"); } } + public virtual bool RequiresRefresh() + { + return false; + } + private string _userDataKey; /// /// Gets the user data key. diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 978fd7fedf..457a0b3abd 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -28,6 +28,9 @@ namespace MediaBrowser.Controller.Entities public List ThemeSongIds { get; set; } public List ThemeVideoIds { get; set; } + [IgnoreDataMember] + public DateTime? DateLastMediaAdded { get; set; } + public Folder() { LinkedChildren = new List(); @@ -55,6 +58,36 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public virtual bool SupportsCumulativeRunTimeTicks + { + get + { + return false; + } + } + + [IgnoreDataMember] + public virtual bool SupportsDateLastMediaAdded + { + get + { + return false; + } + } + + public override bool RequiresRefresh() + { + var baseResult = base.RequiresRefresh(); + + if (SupportsCumulativeRunTimeTicks && !RunTimeTicks.HasValue) + { + baseResult = true; + } + + return baseResult; + } + [IgnoreDataMember] public override string FileNameWithoutExtension { @@ -789,11 +822,6 @@ namespace MediaBrowser.Controller.Entities Logger.Debug("Query requires post-filtering due to ItemSortBy.AiredEpisodeOrder"); return true; } - if (query.SortBy.Contains(ItemSortBy.Album, StringComparer.OrdinalIgnoreCase)) - { - Logger.Debug("Query requires post-filtering due to ItemSortBy.Album"); - return true; - } if (query.SortBy.Contains(ItemSortBy.AlbumArtist, StringComparer.OrdinalIgnoreCase)) { Logger.Debug("Query requires post-filtering due to ItemSortBy.AlbumArtist"); @@ -809,11 +837,6 @@ namespace MediaBrowser.Controller.Entities Logger.Debug("Query requires post-filtering due to ItemSortBy.Budget"); return true; } - if (query.SortBy.Contains(ItemSortBy.DateLastContentAdded, StringComparer.OrdinalIgnoreCase)) - { - Logger.Debug("Query requires post-filtering due to ItemSortBy.DateLastContentAdded"); - return true; - } if (query.SortBy.Contains(ItemSortBy.GameSystem, StringComparer.OrdinalIgnoreCase)) { Logger.Debug("Query requires post-filtering due to ItemSortBy.GameSystem"); @@ -1086,12 +1109,6 @@ namespace MediaBrowser.Controller.Entities return true; } - if (query.AlbumNames.Length > 0) - { - Logger.Debug("Query requires post-filtering due to AlbumNames"); - return true; - } - if (query.ArtistNames.Length > 0) { Logger.Debug("Query requires post-filtering due to ArtistNames"); diff --git a/MediaBrowser.Controller/Entities/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs index 1f680b35f3..c7940c8a97 100644 --- a/MediaBrowser.Controller/Entities/IHasMetadata.cs +++ b/MediaBrowser.Controller/Entities/IHasMetadata.cs @@ -49,5 +49,7 @@ namespace MediaBrowser.Controller.Entities /// /// true if [supports people]; otherwise, false. bool SupportsPeople { get; } + + bool RequiresRefresh(); } } diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 385ee81e9d..5236b0a278 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -137,10 +137,12 @@ namespace MediaBrowser.Controller.Entities public string AncestorWithPresentationUniqueKey { get; set; } public bool GroupByPresentationUniqueKey { get; set; } + public bool EnableTotalRecordCount { get; set; } public InternalItemsQuery() { GroupByPresentationUniqueKey = true; + EnableTotalRecordCount = true; AlbumNames = new string[] { }; ArtistNames = new string[] { }; diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs index bf4c2559c5..7119828e26 100644 --- a/MediaBrowser.Controller/Entities/MusicVideo.cs +++ b/MediaBrowser.Controller/Entities/MusicVideo.cs @@ -9,12 +9,6 @@ namespace MediaBrowser.Controller.Entities { public class MusicVideo : Video, IHasArtist, IHasMusicGenres, IHasProductionLocations, IHasBudget, IHasLookupInfo { - /// - /// Gets or sets the album. - /// - /// The album. - public string Album { get; set; } - /// /// Gets or sets the budget. /// diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 4e6128527c..ac8cc0ee26 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -32,6 +32,15 @@ namespace MediaBrowser.Controller.Entities.TV } } + [IgnoreDataMember] + public override bool SupportsDateLastMediaAdded + { + get + { + return true; + } + } + [IgnoreDataMember] public override Guid? DisplayParentId { diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 680af1843c..f8cdab8ce6 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -48,6 +48,15 @@ namespace MediaBrowser.Controller.Entities.TV } } + [IgnoreDataMember] + public override bool SupportsDateLastMediaAdded + { + get + { + return true; + } + } + public bool DisplaySpecialsWithSeasons { get; set; } public List LocalTrailerIds { get; set; } diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs index 8ce39c6979..b9e997d175 100644 --- a/MediaBrowser.Controller/Entities/UserRootFolder.cs +++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs @@ -102,10 +102,5 @@ namespace MediaBrowser.Controller.Entities LibraryManager.RegisterItem(item); } } - - public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user) - { - // Nothing meaninful here and will only waste resources - } } } diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index cd0c5dc1c7..003cbcfcda 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -39,6 +39,15 @@ namespace MediaBrowser.Controller.Playlists } } + [IgnoreDataMember] + public override bool SupportsCumulativeRunTimeTicks + { + get + { + return true; + } + } + public override bool IsAuthorizedToDelete(User user) { return true; diff --git a/MediaBrowser.Model/LiveTv/ProgramQuery.cs b/MediaBrowser.Model/LiveTv/ProgramQuery.cs index 7a877e3561..0141191c18 100644 --- a/MediaBrowser.Model/LiveTv/ProgramQuery.cs +++ b/MediaBrowser.Model/LiveTv/ProgramQuery.cs @@ -14,8 +14,11 @@ namespace MediaBrowser.Model.LiveTv ChannelIds = new string[] { }; SortBy = new string[] { }; Genres = new string[] { }; + EnableTotalRecordCount = true; } + public bool EnableTotalRecordCount { get; set; } + /// /// Fields to return within the items, in addition to basic information /// diff --git a/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs b/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs index e83a8fda65..0e6d081a1d 100644 --- a/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs +++ b/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs @@ -13,7 +13,14 @@ namespace MediaBrowser.Model.LiveTv public bool? EnableImages { get; set; } public int? ImageTypeLimit { get; set; } public ImageType[] EnableImageTypes { get; set; } - + + public bool EnableTotalRecordCount { get; set; } + + public RecommendedProgramQuery() + { + EnableTotalRecordCount = true; + } + /// /// Gets or sets the user identifier. /// diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index ae48c996a0..218127ab9e 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -310,6 +310,11 @@ namespace MediaBrowser.Providers.Manager return true; } + if (item is MusicVideo) + { + return true; + } + return false; } @@ -390,7 +395,6 @@ namespace MediaBrowser.Providers.Manager return _cachedTask; } - private readonly Task _cachedResult = Task.FromResult(ItemUpdateType.None); /// /// Befores the save. /// @@ -398,9 +402,58 @@ namespace MediaBrowser.Providers.Manager /// if set to true [is full refresh]. /// Type of the current update. /// ItemUpdateType. - protected virtual Task BeforeSave(TItemType item, bool isFullRefresh, ItemUpdateType currentUpdateType) + protected virtual async Task BeforeSave(TItemType item, bool isFullRefresh, ItemUpdateType currentUpdateType) { - return _cachedResult; + var updateType = ItemUpdateType.None; + + updateType |= SaveCumulativeRunTimeTicks(item, isFullRefresh, currentUpdateType); + updateType |= SaveDateLastMediaAdded(item, isFullRefresh, currentUpdateType); + + return updateType; + } + + private ItemUpdateType SaveCumulativeRunTimeTicks(TItemType item, bool isFullRefresh, ItemUpdateType currentUpdateType) + { + var updateType = ItemUpdateType.None; + + if (isFullRefresh || currentUpdateType > ItemUpdateType.None) + { + var folder = item as Folder; + if (folder != null && folder.SupportsCumulativeRunTimeTicks) + { + var ticks = folder.GetRecursiveChildren(i => !i.IsFolder).Select(i => i.RunTimeTicks ?? 0).Sum(); + + if (!folder.RunTimeTicks.HasValue || folder.RunTimeTicks.Value != ticks) + { + folder.RunTimeTicks = ticks; + updateType = ItemUpdateType.MetadataEdit; + } + } + } + + return updateType; + } + + private ItemUpdateType SaveDateLastMediaAdded(TItemType item, bool isFullRefresh, ItemUpdateType currentUpdateType) + { + var updateType = ItemUpdateType.None; + + if (isFullRefresh || currentUpdateType > ItemUpdateType.None) + { + var folder = item as Folder; + if (folder != null && folder.SupportsDateLastMediaAdded) + { + var date = folder.GetRecursiveChildren(i => !i.IsFolder).Select(i => i.DateCreated).Max(); + + if (!folder.DateLastMediaAdded.HasValue || folder.DateLastMediaAdded.Value != date) + { + folder.DateLastMediaAdded = date; + updateType = ItemUpdateType.MetadataEdit; + } + } + } + + return updateType; } /// @@ -420,7 +473,7 @@ namespace MediaBrowser.Providers.Manager : status.DateLastMetadataRefresh ?? default(DateTime); // Run all if either of these flags are true - var runAllProviders = options.ReplaceAllMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || dateLastRefresh == default(DateTime); + var runAllProviders = options.ReplaceAllMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || dateLastRefresh == default(DateTime) || item.RequiresRefresh(); if (!runAllProviders) { diff --git a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs index ad900123ed..2eb65f4e57 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs +++ b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs @@ -62,12 +62,25 @@ namespace MediaBrowser.Providers.Music private IEnumerable GetResultsFromResponse(XmlDocument doc) { - var ns = new XmlNamespaceManager(doc.NameTable); - ns.AddNamespace("mb", "https://musicbrainz.org/ns/mmd-2.0#"); + //var ns = new XmlNamespaceManager(doc.NameTable); + //ns.AddNamespace("mb", "https://musicbrainz.org/ns/mmd-2.0#"); var list = new List(); - var nodes = doc.SelectNodes("//mb:artist-list/mb:artist", ns); + var docElem = doc.DocumentElement; + + if (docElem == null) + { + return list; + } + + var artistList = docElem.FirstChild; + if (artistList == null) + { + return list; + } + + var nodes = artistList.ChildNodes; if (nodes != null) { @@ -79,11 +92,13 @@ namespace MediaBrowser.Providers.Music string mbzId = node.Attributes["id"].Value; - var nameNode = node.SelectSingleNode("//mb:name", ns); - - if (nameNode != null) + foreach (var child in node.ChildNodes.Cast()) { - name = nameNode.InnerText; + if (string.Equals(child.Name, "name", StringComparison.OrdinalIgnoreCase)) + { + name = node.InnerText; + break; + } } if (!string.IsNullOrWhiteSpace(mbzId) && !string.IsNullOrWhiteSpace(name)) diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 0aec3230da..32610a6ad9 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -499,6 +499,16 @@ namespace MediaBrowser.Server.Implementations.Dto } } + if (fields.Contains(ItemFields.CumulativeRunTimeTicks)) + { + dto.CumulativeRunTimeTicks = dto.RunTimeTicks; + } + + if (fields.Contains(ItemFields.DateLastMediaAdded)) + { + dto.DateLastMediaAdded = folder.DateLastMediaAdded; + } + dto.UserData.Played = dto.UserData.PlayedPercentage.HasValue && dto.UserData.PlayedPercentage.Value >= 100; } @@ -1613,9 +1623,7 @@ namespace MediaBrowser.Server.Implementations.Dto { var recursiveItemCount = 0; var unplayed = 0; - long runtime = 0; - DateTime? dateLastMediaAdded = null; double totalPercentPlayed = 0; double totalSyncPercent = 0; var addSyncInfo = fields.Contains(ItemFields.SyncInfo); @@ -1632,15 +1640,6 @@ namespace MediaBrowser.Server.Implementations.Dto // Loop through each recursive child foreach (var child in children) { - if (!dateLastMediaAdded.HasValue) - { - dateLastMediaAdded = child.DateCreated; - } - else - { - dateLastMediaAdded = new[] { dateLastMediaAdded.Value, child.DateCreated }.Max(); - } - var userdata = _userDataRepository.GetUserData(user, child); recursiveItemCount++; @@ -1669,8 +1668,6 @@ namespace MediaBrowser.Server.Implementations.Dto unplayed++; } - runtime += child.RunTimeTicks ?? 0; - if (addSyncInfo) { double percent = 0; @@ -1709,16 +1706,6 @@ namespace MediaBrowser.Server.Implementations.Dto } } } - - if (runtime > 0 && fields.Contains(ItemFields.CumulativeRunTimeTicks)) - { - dto.CumulativeRunTimeTicks = runtime; - } - - if (fields.Contains(ItemFields.DateLastMediaAdded)) - { - dto.DateLastMediaAdded = dateLastMediaAdded; - } } /// diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index f9bf3446f4..c95b301722 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -1354,12 +1354,20 @@ namespace MediaBrowser.Server.Implementations.Library AddUserToQuery(query, query.User); } - var initialResult = ItemRepository.GetItemIds(query); + if (query.EnableTotalRecordCount) + { + var initialResult = ItemRepository.GetItemIds(query); + + return new QueryResult + { + TotalRecordCount = initialResult.TotalRecordCount, + Items = initialResult.Items.Select(GetItemById).Where(i => i != null).ToArray() + }; + } return new QueryResult { - TotalRecordCount = initialResult.TotalRecordCount, - Items = initialResult.Items.Select(GetItemById).Where(i => i != null).ToArray() + Items = ItemRepository.GetItemIdsList(query).Select(GetItemById).Where(i => i != null).ToArray() }; } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index c5920d3d6a..b5e8ad79a5 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -760,7 +760,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV if (info.IsMovie) { var customRecordingPath = config.MovieRecordingPath; - if ((string.IsNullOrWhiteSpace(customRecordingPath) || string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase)) && config.EnableRecordingSubfolders) + var allowSubfolder = true; + if (!string.IsNullOrWhiteSpace(customRecordingPath)) + { + allowSubfolder = string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase); + recordPath = customRecordingPath; + } + + if (allowSubfolder && config.EnableRecordingSubfolders) { recordPath = Path.Combine(recordPath, "Movies"); } @@ -775,7 +782,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV else if (info.IsSeries) { var customRecordingPath = config.SeriesRecordingPath; - if ((string.IsNullOrWhiteSpace(customRecordingPath) || string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase)) && config.EnableRecordingSubfolders) + var allowSubfolder = true; + if (!string.IsNullOrWhiteSpace(customRecordingPath)) + { + allowSubfolder = string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase); + recordPath = customRecordingPath; + } + + if (allowSubfolder && config.EnableRecordingSubfolders) { recordPath = Path.Combine(recordPath, "Series"); } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index eec8328f89..175eed66c0 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -886,7 +886,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv StartIndex = query.StartIndex, Limit = query.Limit, SortBy = query.SortBy, - SortOrder = query.SortOrder ?? SortOrder.Ascending + SortOrder = query.SortOrder ?? SortOrder.Ascending, + EnableTotalRecordCount = query.EnableTotalRecordCount }; if (query.HasAired.HasValue) @@ -924,7 +925,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv IsAiring = query.IsAiring, IsMovie = query.IsMovie, IsSports = query.IsSports, - IsKids = query.IsKids + IsKids = query.IsKids, + EnableTotalRecordCount = query.EnableTotalRecordCount }; if (query.HasAired.HasValue) @@ -1263,11 +1265,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv private async Task CleanDatabaseInternal(List currentIdList, string[] validTypes, IProgress progress, CancellationToken cancellationToken) { - var list = _itemRepo.GetItemIds(new InternalItemsQuery + var list = _itemRepo.GetItemIdsList(new InternalItemsQuery { IncludeItemTypes = validTypes - }).Items.ToList(); + }).ToList(); var numComplete = 0; diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 09739f8a9d..180bd35479 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -85,7 +85,7 @@ namespace MediaBrowser.Server.Implementations.Persistence private IDbCommand _updateInheritedRatingCommand; private IDbCommand _updateInheritedTagsCommand; - public const int LatestSchemaVersion = 76; + public const int LatestSchemaVersion = 77; /// /// Initializes a new instance of the class. @@ -235,6 +235,9 @@ namespace MediaBrowser.Server.Implementations.Persistence _connection.AddColumn(Logger, "TypedBaseItems", "SlugName", "Text"); _connection.AddColumn(Logger, "TypedBaseItems", "OriginalTitle", "Text"); _connection.AddColumn(Logger, "TypedBaseItems", "PrimaryVersionId", "Text"); + _connection.AddColumn(Logger, "TypedBaseItems", "DateLastMediaAdded", "DATETIME"); + _connection.AddColumn(Logger, "TypedBaseItems", "Album", "Text"); + _connection.AddColumn(Logger, "UserDataKeys", "Priority", "INT"); string[] postQueries = @@ -351,7 +354,9 @@ namespace MediaBrowser.Server.Implementations.Persistence "TrailerTypes", "DateModifiedDuringLastRefresh", "OriginalTitle", - "PrimaryVersionId" + "PrimaryVersionId", + "DateLastMediaAdded", + "Album" }; private readonly string[] _mediaStreamSaveColumns = @@ -463,7 +468,9 @@ namespace MediaBrowser.Server.Implementations.Persistence "PresentationUniqueKey", "SlugName", "OriginalTitle", - "PrimaryVersionId" + "PrimaryVersionId", + "DateLastMediaAdded", + "Album" }; _saveItemCommand = _connection.CreateCommand(); _saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values ("; @@ -824,6 +831,18 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveItemCommand.GetParameter(index++).Value = null; } + var folder = item as Folder; + if (folder != null && folder.DateLastMediaAdded.HasValue) + { + _saveItemCommand.GetParameter(index++).Value = folder.DateLastMediaAdded.Value; + } + else + { + _saveItemCommand.GetParameter(index++).Value = null; + } + + _saveItemCommand.GetParameter(index++).Value = item.Album; + _saveItemCommand.Transaction = transaction; _saveItemCommand.ExecuteNonQuery(); @@ -1217,6 +1236,17 @@ namespace MediaBrowser.Server.Implementations.Persistence } } + var folder = item as Folder; + if (folder != null && !reader.IsDBNull(54)) + { + folder.DateLastMediaAdded = reader.GetDateTime(54).ToUniversalTime(); + } + + if (!reader.IsDBNull(55)) + { + item.Album = reader.GetString(55); + } + return item; } @@ -1777,6 +1807,10 @@ namespace MediaBrowser.Server.Implementations.Persistence { return new Tuple("played", false); } + if (string.Equals(name, ItemSortBy.DateLastContentAdded, StringComparison.OrdinalIgnoreCase)) + { + return new Tuple("DateLastMediaAdded", false); + } return new Tuple(name, false); } @@ -2484,7 +2518,7 @@ namespace MediaBrowser.Server.Implementations.Persistence if (query.MediaTypes.Length == 1) { whereClauses.Add("MediaType=@MediaTypes"); - cmd.Parameters.Add(cmd, "@MediaTypes", DbType.String).Value = query.MediaTypes[0].ToString(); + cmd.Parameters.Add(cmd, "@MediaTypes", DbType.String).Value = query.MediaTypes[0]; } if (query.MediaTypes.Length > 1) { @@ -2493,6 +2527,26 @@ namespace MediaBrowser.Server.Implementations.Persistence whereClauses.Add("MediaType in (" + val + ")"); } + if (query.AlbumNames.Length > 0) + { + var clause = "("; + + var index = 0; + foreach (var name in query.AlbumNames) + { + if (index > 0) + { + clause += " OR "; + } + clause += "Album=@AlbumName" + index; + index++; + cmd.Parameters.Add(cmd, "@AlbumName" + index, DbType.String).Value = name; + } + + clause += ")"; + whereClauses.Add(clause); + } + //var enableItemsByName = query.IncludeItemsByName ?? query.IncludeItemTypes.Length > 0; var enableItemsByName = query.IncludeItemsByName ?? false; diff --git a/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs index 68cd44ec94..5c51f5e0fc 100644 --- a/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs +++ b/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs @@ -49,10 +49,10 @@ namespace MediaBrowser.Server.Implementations.Sorting if (folder != null) { - return folder.GetRecursiveChildren(User, i => !i.IsFolder) - .Select(i => i.DateCreated) - .OrderByDescending(i => i) - .FirstOrDefault(); + if (folder.DateLastMediaAdded.HasValue) + { + return folder.DateLastMediaAdded.Value; + } } return x.DateCreated; diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index f544a149d8..366d4b6083 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -142,8 +142,6 @@ - - diff --git a/MediaBrowser.ServerApplication/Native/WindowsApp.cs b/MediaBrowser.ServerApplication/Native/WindowsApp.cs index 808c7558ec..271b02d9a7 100644 --- a/MediaBrowser.ServerApplication/Native/WindowsApp.cs +++ b/MediaBrowser.ServerApplication/Native/WindowsApp.cs @@ -158,9 +158,9 @@ namespace MediaBrowser.ServerApplication.Native info.FFMpegFilename = "ffmpeg.exe"; info.FFProbeFilename = "ffprobe.exe"; - info.Version = "20160401"; + info.Version = "20160508"; info.ArchiveType = "7z"; - info.IsEmbedded = true; + info.IsEmbedded = false; info.DownloadUrls = GetDownloadUrls(); return info; @@ -212,11 +212,18 @@ namespace MediaBrowser.ServerApplication.Native switch (Environment.SystemArchitecture) { case Architecture.X86_X64: - return new[] { "MediaBrowser.ServerApplication.ffmpeg.ffmpegx64.7z" }; + return new[] + { + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160508-win64.7z", + "https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-20160508-git-caee88d-win64-static.7z" + }; case Architecture.X86: - return new[] { "MediaBrowser.ServerApplication.ffmpeg.ffmpegx86.7z" }; + return new[] + { + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160508-win32.7z", + "https://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20160508-git-caee88d-win32-static.7z" + }; } - return new string[] { }; } } diff --git a/MediaBrowser.ServerApplication/ffmpeg/ffmpegx64.7z.REMOVED.git-id b/MediaBrowser.ServerApplication/ffmpeg/ffmpegx64.7z.REMOVED.git-id deleted file mode 100644 index b0542b75f3..0000000000 --- a/MediaBrowser.ServerApplication/ffmpeg/ffmpegx64.7z.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -9dc10b022537738edce7eb71aa8dd4adbfee2c7b \ No newline at end of file diff --git a/MediaBrowser.ServerApplication/ffmpeg/ffmpegx86.7z.REMOVED.git-id b/MediaBrowser.ServerApplication/ffmpeg/ffmpegx86.7z.REMOVED.git-id deleted file mode 100644 index 3939ec44d6..0000000000 --- a/MediaBrowser.ServerApplication/ffmpeg/ffmpegx86.7z.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -00fa1afa35fbd0a7e97ad7956e42ae17f6882f64 \ No newline at end of file