From dc7a59c73a1e163f122780bdd8a5ca6cad96eb06 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 23 Jul 2017 18:29:53 -0400 Subject: ensure custom css is loaded last --- .../Data/SqliteItemRepository.cs | 40 ---------------------- 1 file changed, 40 deletions(-) (limited to 'Emby.Server.Implementations/Data/SqliteItemRepository.cs') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 52abbae3e2..e4c05bce26 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -251,7 +251,6 @@ namespace Emby.Server.Implementations.Data AddColumn(db, "TypedBaseItems", "SeriesId", "GUID", existingColumnNames); AddColumn(db, "TypedBaseItems", "ExternalSeriesId", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "Tagline", "Text", existingColumnNames); - AddColumn(db, "TypedBaseItems", "Keywords", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "ProviderIds", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "Images", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "ProductionLocations", "Text", existingColumnNames); @@ -454,7 +453,6 @@ namespace Emby.Server.Implementations.Data "InheritedTags", "ExternalSeriesId", "Tagline", - "Keywords", "ProviderIds", "Images", "ProductionLocations", @@ -578,7 +576,6 @@ namespace Emby.Server.Implementations.Data "SeriesId", "ExternalSeriesId", "Tagline", - "Keywords", "ProviderIds", "Images", "ProductionLocations", @@ -1011,15 +1008,6 @@ namespace Emby.Server.Implementations.Data saveItemStatement.TryBind("@ExternalSeriesId", item.ExternalSeriesId); saveItemStatement.TryBind("@Tagline", item.Tagline); - if (item.Keywords.Count > 0) - { - saveItemStatement.TryBind("@Keywords", string.Join("|", item.Keywords.ToArray())); - } - else - { - saveItemStatement.TryBindNull("@Keywords"); - } - saveItemStatement.TryBind("@ProviderIds", SerializeProviderIds(item)); saveItemStatement.TryBind("@Images", SerializeImages(item)); @@ -1874,15 +1862,6 @@ namespace Emby.Server.Implementations.Data index++; } - if (HasField(query, ItemFields.Keywords)) - { - if (!reader.IsDBNull(index)) - { - item.Keywords = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); - } - index++; - } - if (!reader.IsDBNull(index)) { DeserializeProviderIds(reader.GetString(index), item); @@ -2287,7 +2266,6 @@ namespace Emby.Server.Implementations.Data switch (name) { case ItemFields.HomePageUrl: - case ItemFields.Keywords: case ItemFields.DisplayMediaType: case ItemFields.CustomRating: case ItemFields.ProductionLocations: @@ -4221,23 +4199,6 @@ namespace Emby.Server.Implementations.Data whereClauses.Add(clause); } - if (query.Keywords.Length > 0) - { - var clauses = new List(); - var index = 0; - foreach (var item in query.Keywords) - { - clauses.Add("@Keyword" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=5)"); - if (statement != null) - { - statement.TryBind("@Keyword" + index, GetCleanValue(item)); - } - index++; - } - var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")"; - whereClauses.Add(clause); - } - if (query.OfficialRatings.Length > 0) { var clauses = new List(); @@ -5435,7 +5396,6 @@ namespace Emby.Server.Implementations.Data list.AddRange(item.Genres.Select(i => new Tuple(2, i))); list.AddRange(item.Studios.Select(i => new Tuple(3, i))); list.AddRange(item.Tags.Select(i => new Tuple(4, i))); - list.AddRange(item.Keywords.Select(i => new Tuple(5, i))); return list; } -- cgit v1.2.3 From a46840534f943100e22fd869c5c30d4b5516dc1d Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 25 Jul 2017 14:32:03 -0400 Subject: 3.2.26.3 --- Emby.Common.Implementations/Net/UdpSocket.cs | 4 - Emby.Drawing/ImageProcessor.cs | 12 +++ .../Data/SqliteItemRepository.cs | 117 +++++++++++++++++---- .../Library/ResolverHelper.cs | 20 ++-- MediaBrowser.Controller/Library/ItemResolveArgs.cs | 15 --- .../MediaEncoding/EncodingHelper.cs | 4 + SharedVersion.cs | 2 +- 7 files changed, 123 insertions(+), 51 deletions(-) (limited to 'Emby.Server.Implementations/Data/SqliteItemRepository.cs') diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index df1099d3df..542d16d24e 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -37,8 +37,6 @@ namespace Emby.Common.Implementations.Net private TaskCompletionSource _currentReceiveTaskCompletionSource; private TaskCompletionSource _currentSendTaskCompletionSource; - private readonly SemaphoreSlim _sendLock = new SemaphoreSlim(1, 1); - public UdpSocket(Socket socket, int localPort, IPAddress ip) { if (socket == null) throw new ArgumentNullException("socket"); @@ -234,8 +232,6 @@ namespace Emby.Common.Implementations.Net if (socket != null) socket.Dispose(); - _sendLock.Dispose(); - var tcs = _currentReceiveTaskCompletionSource; if (tcs != null) { diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index be45912235..88ead3a5fc 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -171,6 +171,13 @@ namespace Emby.Drawing return _imageEncoder.SupportedOutputFormats; } + private static readonly string[] TransparentImageTypes = new string[] { ".png", ".webp" }; + private bool SupportsTransparency(string path) + { + return TransparentImageTypes.Contains(Path.GetExtension(path) ?? string.Empty); + ; + } + public async Task> ProcessImage(ImageProcessingOptions options) { if (options == null) @@ -260,6 +267,11 @@ namespace Emby.Drawing item = _libraryManager().GetItemById(options.ItemId); } + if (options.CropWhiteSpace && !SupportsTransparency(originalImagePath)) + { + options.CropWhiteSpace = false; + } + var resultPath = _imageEncoder.EncodeImage(originalImagePath, dateModified, tmpPath, autoOrient, orientation, quality, options, outputFormat); if (string.Equals(resultPath, originalImagePath, StringComparison.OrdinalIgnoreCase)) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index e4c05bce26..528acd069a 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -233,7 +233,6 @@ namespace Emby.Server.Implementations.Data AddColumn(db, "TypedBaseItems", "InheritedParentalRatingValue", "INT", existingColumnNames); AddColumn(db, "TypedBaseItems", "UnratedType", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "TopParentId", "Text", existingColumnNames); - AddColumn(db, "TypedBaseItems", "IsItemByName", "BIT", existingColumnNames); AddColumn(db, "TypedBaseItems", "TrailerTypes", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "CriticRating", "Float", existingColumnNames); AddColumn(db, "TypedBaseItems", "InheritedTags", "Text", existingColumnNames); @@ -558,7 +557,6 @@ namespace Emby.Server.Implementations.Data "IsFolder", "UnratedType", "TopParentId", - "IsItemByName", "TrailerTypes", "CriticRating", "InheritedTags", @@ -897,15 +895,6 @@ namespace Emby.Server.Implementations.Data saveItemStatement.TryBindNull("@TopParentId"); } - var isByName = false; - var byName = item as IItemByName; - if (byName != null) - { - var dualAccess = item as IHasDualAccess; - isByName = dualAccess == null || dualAccess.IsAccessedByName; - } - saveItemStatement.TryBind("@IsItemByName", isByName); - var trailer = item as Trailer; if (trailer != null && trailer.TrailerTypes.Count > 0) { @@ -1656,7 +1645,11 @@ namespace Emby.Server.Implementations.Data if (!reader.IsDBNull(index)) { - item.Audio = (ProgramAudio)Enum.Parse(typeof(ProgramAudio), reader.GetString(index), true); + ProgramAudio audio; + if (Enum.TryParse(reader.GetString(index), true, out audio)) + { + item.Audio = audio; + } } index++; @@ -1687,7 +1680,17 @@ namespace Emby.Server.Implementations.Data { if (!reader.IsDBNull(index)) { - item.LockedFields = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => (MetadataFields)Enum.Parse(typeof(MetadataFields), i, true)).ToList(); + item.LockedFields = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).Select( + i => + { + MetadataFields parsedValue; + + if (Enum.TryParse(i, true, out parsedValue)) + { + return parsedValue; + } + return (MetadataFields?)null; + }).Where(i => i.HasValue).Select(i => i.Value).ToList(); } index++; } @@ -1717,7 +1720,18 @@ namespace Emby.Server.Implementations.Data { if (!reader.IsDBNull(index)) { - trailer.TrailerTypes = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => (TrailerType)Enum.Parse(typeof(TrailerType), i, true)).ToList(); + trailer.TrailerTypes = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).Select( + i => + { + TrailerType parsedValue; + + if (Enum.TryParse(i, true, out parsedValue)) + { + return parsedValue; + } + return (TrailerType?)null; + + }).Where(i => i.HasValue).Select(i => i.Value).ToList(); } } index++; @@ -1912,7 +1926,11 @@ namespace Emby.Server.Implementations.Data if (!reader.IsDBNull(index)) { - item.ExtraType = (ExtraType)Enum.Parse(typeof(ExtraType), reader.GetString(index), true); + ExtraType extraType; + if (Enum.TryParse(reader.GetString(index), true, out extraType)) + { + item.ExtraType = extraType; + } } index++; @@ -4444,21 +4462,27 @@ namespace Emby.Server.Implementations.Data } } - //var enableItemsByName = query.IncludeItemsByName ?? query.IncludeItemTypes.Length > 0; - var enableItemsByName = query.IncludeItemsByName ?? false; + + var includedItemByNameTypes = GetItemByNameTypesInQuery(query).SelectMany(MapIncludeItemTypes).ToList(); + var enableItemsByName = (query.IncludeItemsByName ?? false) && includedItemByNameTypes.Count > 0; var queryTopParentIds = query.TopParentIds.Where(IsValidId).ToArray(); if (queryTopParentIds.Length == 1) { - if (enableItemsByName) + if (enableItemsByName && includedItemByNameTypes.Count == 1) { - whereClauses.Add("(TopParentId=@TopParentId or IsItemByName=@IsItemByName)"); + whereClauses.Add("(TopParentId=@TopParentId or Type=@IncludedItemByNameType)"); if (statement != null) { - statement.TryBind("@IsItemByName", true); + statement.TryBind("@IncludedItemByNameType", includedItemByNameTypes[0]); } } + else if (enableItemsByName && includedItemByNameTypes.Count > 1) + { + var itemByNameTypeVal = string.Join(",", includedItemByNameTypes.Select(i => "'" + i + "'").ToArray()); + whereClauses.Add("(TopParentId=@TopParentId or Type in (" + itemByNameTypeVal + "))"); + } else { whereClauses.Add("(TopParentId=@TopParentId)"); @@ -4472,14 +4496,19 @@ namespace Emby.Server.Implementations.Data { var val = string.Join(",", queryTopParentIds.Select(i => "'" + i + "'").ToArray()); - if (enableItemsByName) + if (enableItemsByName && includedItemByNameTypes.Count == 1) { - whereClauses.Add("(IsItemByName=@IsItemByName or TopParentId in (" + val + "))"); + whereClauses.Add("(Type=@IncludedItemByNameType or TopParentId in (" + val + "))"); if (statement != null) { - statement.TryBind("@IsItemByName", true); + statement.TryBind("@IncludedItemByNameType", includedItemByNameTypes[0]); } } + else if (enableItemsByName && includedItemByNameTypes.Count > 1) + { + var itemByNameTypeVal = string.Join(",", includedItemByNameTypes.Select(i => "'" + i + "'").ToArray()); + whereClauses.Add("(Type in (" + itemByNameTypeVal + ") or TopParentId in (" + val + "))"); + } else { whereClauses.Add("(TopParentId in (" + val + "))"); @@ -4559,6 +4588,48 @@ namespace Emby.Server.Implementations.Data return whereClauses; } + private List GetItemByNameTypesInQuery(InternalItemsQuery query) + { + var list = new List(); + + if (IsTypeInQuery(typeof(Person).Name, query)) + { + list.Add(typeof(Person).Name); + } + if (IsTypeInQuery(typeof(Genre).Name, query)) + { + list.Add(typeof(Genre).Name); + } + if (IsTypeInQuery(typeof(MusicGenre).Name, query)) + { + list.Add(typeof(MusicGenre).Name); + } + if (IsTypeInQuery(typeof(GameGenre).Name, query)) + { + list.Add(typeof(GameGenre).Name); + } + if (IsTypeInQuery(typeof(MusicArtist).Name, query)) + { + list.Add(typeof(MusicArtist).Name); + } + if (IsTypeInQuery(typeof(Studio).Name, query)) + { + list.Add(typeof(Studio).Name); + } + + return list; + } + + private bool IsTypeInQuery(string type, InternalItemsQuery query) + { + if (query.ExcludeItemTypes.Contains(type, StringComparer.OrdinalIgnoreCase)) + { + return false; + } + + return query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(type, StringComparer.OrdinalIgnoreCase); + } + private string GetCleanValue(string value) { if (string.IsNullOrWhiteSpace(value)) diff --git a/Emby.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs index 24dc1104aa..d0096de0c5 100644 --- a/Emby.Server.Implementations/Library/ResolverHelper.cs +++ b/Emby.Server.Implementations/Library/ResolverHelper.cs @@ -46,7 +46,7 @@ namespace Emby.Server.Implementations.Library var fileInfo = directoryService.GetFile(item.Path); SetDateCreated(item, fileSystem, fileInfo); - EnsureName(item, fileInfo); + EnsureName(item, item.Path, fileInfo); } /// @@ -73,7 +73,7 @@ namespace Emby.Server.Implementations.Library item.Id = libraryManager.GetNewItemId(item.Path, item.GetType()); // Make sure the item has a name - EnsureName(item, args.FileInfo); + EnsureName(item, item.Path, args.FileInfo); item.IsLocked = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 || item.GetParents().Any(i => i.IsLocked); @@ -85,14 +85,14 @@ namespace Emby.Server.Implementations.Library /// /// Ensures the name. /// - /// The item. - /// The file information. - private static void EnsureName(BaseItem item, FileSystemMetadata fileInfo) + private static void EnsureName(BaseItem item, string fullPath, FileSystemMetadata fileInfo) { // If the subclass didn't supply a name, add it here - if (string.IsNullOrEmpty(item.Name) && !string.IsNullOrEmpty(item.Path)) + if (string.IsNullOrEmpty(item.Name) && !string.IsNullOrEmpty(fullPath)) { - item.Name = GetDisplayName(fileInfo.Name, fileInfo.IsDirectory); + var fileName = fileInfo == null ? Path.GetFileName(fullPath) : fileInfo.Name; + + item.Name = GetDisplayName(fileName, fileInfo != null && fileInfo.IsDirectory); } } @@ -170,7 +170,11 @@ namespace Emby.Server.Implementations.Library if (config.UseFileCreationTimeForDateAdded) { - item.DateCreated = fileSystem.GetCreationTimeUtc(info); + // directoryService.getFile may return null + if (info != null) + { + item.DateCreated = fileSystem.GetCreationTimeUtc(info); + } } else { diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index 3ddda81cd0..76b6d87689 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -237,21 +237,6 @@ namespace MediaBrowser.Controller.Library return null; } - /// - /// Determines whether [contains meta file by name] [the specified name]. - /// - /// The name. - /// true if [contains meta file by name] [the specified name]; otherwise, false. - public bool ContainsMetaFileByName(string name) - { - if (string.IsNullOrEmpty(name)) - { - throw new ArgumentNullException(); - } - - return GetFileSystemEntryByName(name) != null; - } - /// /// Determines whether [contains file system entry by name] [the specified name]. /// diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 44bdafc5b2..42f0dda160 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -205,6 +205,10 @@ namespace MediaBrowser.Controller.MediaEncoding { return null; } + if (string.Equals(container, "rmvb", StringComparison.OrdinalIgnoreCase)) + { + return null; + } // Seeing reported failures here, not sure yet if this is related to specfying input format if (string.Equals(container, "m4v", StringComparison.OrdinalIgnoreCase)) diff --git a/SharedVersion.cs b/SharedVersion.cs index f4d096f5ac..73ef4d4f59 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.26.2")] +[assembly: AssemblyVersion("3.2.26.3")] -- cgit v1.2.3 From f7f0d3eba0e7aeb21a8c6404919408585258f57b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 28 Jul 2017 12:16:06 -0400 Subject: 3.2.26.6 --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 7 ------- MediaBrowser.WebDashboard/Api/PackageCreator.cs | 2 -- SharedVersion.cs | 2 +- 3 files changed, 1 insertion(+), 10 deletions(-) (limited to 'Emby.Server.Implementations/Data/SqliteItemRepository.cs') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 528acd069a..3e2dbeefd4 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -190,7 +190,6 @@ namespace Emby.Server.Implementations.Data AddColumn(db, "TypedBaseItems", "IsLocked", "BIT", existingColumnNames); AddColumn(db, "TypedBaseItems", "Name", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "OfficialRating", "Text", existingColumnNames); - AddColumn(db, "TypedBaseItems", "MediaType", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "Overview", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "ParentIndexNumber", "INT", existingColumnNames); @@ -200,28 +199,22 @@ namespace Emby.Server.Implementations.Data AddColumn(db, "TypedBaseItems", "Genres", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "SortName", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "ForcedSortName", "Text", existingColumnNames); - AddColumn(db, "TypedBaseItems", "RunTimeTicks", "BIGINT", existingColumnNames); - AddColumn(db, "TypedBaseItems", "HomePageUrl", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "DisplayMediaType", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "DateCreated", "DATETIME", existingColumnNames); AddColumn(db, "TypedBaseItems", "DateModified", "DATETIME", existingColumnNames); - AddColumn(db, "TypedBaseItems", "IsSeries", "BIT", existingColumnNames); AddColumn(db, "TypedBaseItems", "IsLive", "BIT", existingColumnNames); AddColumn(db, "TypedBaseItems", "IsNews", "BIT", existingColumnNames); AddColumn(db, "TypedBaseItems", "IsPremiere", "BIT", existingColumnNames); - AddColumn(db, "TypedBaseItems", "EpisodeTitle", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "IsRepeat", "BIT", existingColumnNames); - AddColumn(db, "TypedBaseItems", "PreferredMetadataLanguage", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "PreferredMetadataCountryCode", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "IsHD", "BIT", existingColumnNames); AddColumn(db, "TypedBaseItems", "ExternalEtag", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "DateLastRefreshed", "DATETIME", existingColumnNames); - AddColumn(db, "TypedBaseItems", "DateLastSaved", "DATETIME", existingColumnNames); AddColumn(db, "TypedBaseItems", "IsInMixedFolder", "BIT", existingColumnNames); AddColumn(db, "TypedBaseItems", "LockedFields", "Text", existingColumnNames); diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index da43dd63a5..bd9ef3e9f5 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -39,8 +39,6 @@ namespace MediaBrowser.WebDashboard.Api if (resourceStream != null) { - // Don't apply any caching for html pages - // jQuery ajax doesn't seem to handle if-modified-since correctly if (IsFormat(virtualPath, "html")) { if (IsCoreHtml(virtualPath)) diff --git a/SharedVersion.cs b/SharedVersion.cs index e799ddfeb6..4926fb5745 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.26.5")] +[assembly: AssemblyVersion("3.2.26.6")] -- cgit v1.2.3 From 4e52c027bcc2dc4cd100d450a7195233e48fb5ff Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 1 Aug 2017 12:45:57 -0400 Subject: improve nextup queries --- .../Data/SqliteItemRepository.cs | 23 +++ .../HttpServer/HttpResultFactory.cs | 188 ++++++--------------- .../Library/UserViewManager.cs | 39 ++++- .../LiveTv/EmbyTV/EmbyTV.cs | 3 +- .../LiveTv/LiveTvManager.cs | 9 +- .../Session/SessionManager.cs | 4 +- Emby.Server.Implementations/TV/TVSeriesManager.cs | 51 ++++-- .../UserViews/CollectionFolderImageProvider.cs | 4 +- .../UserViews/DynamicImageProvider.cs | 8 +- MediaBrowser.Api/FilterService.cs | 4 +- MediaBrowser.Api/TvShowsService.cs | 4 +- MediaBrowser.Controller/Entities/Folder.cs | 33 +++- .../Entities/InternalItemsQuery.cs | 1 + MediaBrowser.Controller/Entities/TV/Series.cs | 2 +- MediaBrowser.Controller/Entities/UserView.cs | 8 +- MediaBrowser.Controller/Playlists/Playlist.cs | 5 +- MediaBrowser.Model/Querying/ItemFields.cs | 3 +- MediaBrowser.Server.Mono/Program.cs | 2 +- 18 files changed, 198 insertions(+), 193 deletions(-) (limited to 'Emby.Server.Implementations/Data/SqliteItemRepository.cs') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 3e2dbeefd4..80a5defd67 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -2622,6 +2622,11 @@ namespace Emby.Server.Implementations.Data groups.Add("PresentationUniqueKey"); } + if (query.GroupBySeriesPresentationUniqueKey) + { + groups.Add("SeriesPresentationUniqueKey"); + } + if (groups.Count > 0) { return " Group by " + string.Join(",", groups.ToArray()); @@ -2934,6 +2939,10 @@ namespace Emby.Server.Implementations.Data { commandText += " select count (distinct PresentationUniqueKey)" + GetFromText(); } + else if (query.GroupBySeriesPresentationUniqueKey) + { + commandText += " select count (distinct SeriesPresentationUniqueKey)" + GetFromText(); + } else { commandText += " select count (guid)" + GetFromText(); @@ -3079,6 +3088,11 @@ namespace Emby.Server.Implementations.Data } if (string.Equals(name, ItemSortBy.DatePlayed, StringComparison.OrdinalIgnoreCase)) { + if (query.GroupBySeriesPresentationUniqueKey) + { + return new Tuple("MAX(LastPlayedDate)", false); + } + return new Tuple("LastPlayedDate", false); } if (string.Equals(name, ItemSortBy.PlayCount, StringComparison.OrdinalIgnoreCase)) @@ -3353,6 +3367,10 @@ namespace Emby.Server.Implementations.Data { commandText += " select count (distinct PresentationUniqueKey)" + GetFromText(); } + else if (query.GroupBySeriesPresentationUniqueKey) + { + commandText += " select count (distinct SeriesPresentationUniqueKey)" + GetFromText(); + } else { commandText += " select count (guid)" + GetFromText(); @@ -4640,6 +4658,11 @@ namespace Emby.Server.Implementations.Data return false; } + if (query.GroupBySeriesPresentationUniqueKey) + { + return false; + } + if (!string.IsNullOrWhiteSpace(query.PresentationUniqueKey)) { return false; diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 396bd8e88d..7bd8fe2bf9 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -6,19 +6,16 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; -using System.IO.Compression; using System.Net; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; using System.Xml; -using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.Services; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; using IRequest = MediaBrowser.Model.Services.IRequest; using MimeTypes = MediaBrowser.Model.Net.MimeTypes; -using StreamWriter = Emby.Server.Implementations.HttpServer.StreamWriter; namespace Emby.Server.Implementations.HttpServer { @@ -193,50 +190,37 @@ namespace Emby.Server.Implementations.HttpServer /// public object ToOptimizedResult(IRequest request, T dto) { - var compressionType = GetCompressionType(request); - if (compressionType == null) - { - var contentType = request.ResponseContentType; - - switch (GetRealContentType(contentType)) - { - case "application/xml": - case "text/xml": - case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml - return SerializeToXmlString(dto); - - case "application/json": - case "text/json": - return _jsonSerializer.SerializeToString(dto); - } - } + var contentType = request.ResponseContentType; - // Do not use the memoryStreamFactory here, they don't place nice with compression - using (var ms = new MemoryStream()) + switch (GetRealContentType(contentType)) { - var contentType = request.ResponseContentType; - var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType); + case "application/xml": + case "text/xml": + case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml + return SerializeToXmlString(dto); - writerFn(dto, ms); + case "application/json": + case "text/json": + return _jsonSerializer.SerializeToString(dto); + default: + { + var ms = new MemoryStream(); + var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType); - ms.Position = 0; + writerFn(dto, ms); + + ms.Position = 0; - var responseHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (string.Equals(request.Verb, "head", StringComparison.OrdinalIgnoreCase)) + { + return GetHttpResult(new byte[] { }, contentType, true); + } - return GetCompressedResult(ms, compressionType, responseHeaders, false, request.ResponseContentType).Result; + return GetHttpResult(ms, contentType, true); + } } } - private static Stream GetCompressionStream(Stream outputStream, string compressionType) - { - if (compressionType == "deflate") - return new DeflateStream(outputStream, CompressionMode.Compress, true); - if (compressionType == "gzip") - return new GZipStream(outputStream, CompressionMode.Compress, true); - - throw new NotSupportedException(compressionType); - } - public static string GetRealContentType(string contentType) { return contentType == null @@ -568,123 +552,47 @@ namespace Emby.Server.Implementations.HttpServer var contentType = options.ContentType; var responseHeaders = options.ResponseHeaders; - var requestedCompressionType = GetCompressionType(requestContext); + //var requestedCompressionType = GetCompressionType(requestContext); - if (!compress || string.IsNullOrEmpty(requestedCompressionType)) - { - var rangeHeader = requestContext.Headers.Get("Range"); - - if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path)) - { - return new FileWriter(options.Path, contentType, rangeHeader, _logger, _fileSystem) - { - OnComplete = options.OnComplete, - OnError = options.OnError, - FileShare = options.FileShare - }; - } - - if (!string.IsNullOrWhiteSpace(rangeHeader)) - { - var stream = await factoryFn().ConfigureAwait(false); - - return new RangeRequestWriter(rangeHeader, stream, contentType, isHeadRequest, _logger) - { - OnComplete = options.OnComplete - }; - } - else - { - var stream = await factoryFn().ConfigureAwait(false); - - responseHeaders["Content-Length"] = stream.Length.ToString(UsCulture); - - if (isHeadRequest) - { - stream.Dispose(); - - return GetHttpResult(new byte[] { }, contentType, true); - } - - return new StreamWriter(stream, contentType, _logger) - { - OnComplete = options.OnComplete, - OnError = options.OnError - }; - } - } + var rangeHeader = requestContext.Headers.Get("Range"); - using (var stream = await factoryFn().ConfigureAwait(false)) + if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path)) { - return await GetCompressedResult(stream, requestedCompressionType, responseHeaders, isHeadRequest, contentType).ConfigureAwait(false); + return new FileWriter(options.Path, contentType, rangeHeader, _logger, _fileSystem) + { + OnComplete = options.OnComplete, + OnError = options.OnError, + FileShare = options.FileShare + }; } - } - private async Task GetCompressedResult(Stream stream, - string requestedCompressionType, - IDictionary responseHeaders, - bool isHeadRequest, - string contentType) - { - using (var reader = new MemoryStream()) + if (!string.IsNullOrWhiteSpace(rangeHeader)) { - await stream.CopyToAsync(reader).ConfigureAwait(false); - - reader.Position = 0; - var content = reader.ToArray(); + var stream = await factoryFn().ConfigureAwait(false); - if (content.Length >= 1024) + return new RangeRequestWriter(rangeHeader, stream, contentType, isHeadRequest, _logger) { - content = Compress(content, requestedCompressionType); - responseHeaders["Content-Encoding"] = requestedCompressionType; - } + OnComplete = options.OnComplete + }; + } + else + { + var stream = await factoryFn().ConfigureAwait(false); - responseHeaders["Vary"] = "Accept-Encoding"; - responseHeaders["Content-Length"] = content.Length.ToString(UsCulture); + responseHeaders["Content-Length"] = stream.Length.ToString(UsCulture); if (isHeadRequest) { + stream.Dispose(); + return GetHttpResult(new byte[] { }, contentType, true); } - return GetHttpResult(content, contentType, true, responseHeaders); - } - } - - private byte[] Compress(byte[] bytes, string compressionType) - { - if (compressionType == "deflate") - return Deflate(bytes); - - if (compressionType == "gzip") - return GZip(bytes); - - throw new NotSupportedException(compressionType); - } - - private byte[] Deflate(byte[] bytes) - { - // In .NET FX incompat-ville, you can't access compressed bytes without closing DeflateStream - // Which means we must use MemoryStream since you have to use ToArray() on a closed Stream - using (var ms = new MemoryStream()) - using (var zipStream = new DeflateStream(ms, CompressionMode.Compress)) - { - zipStream.Write(bytes, 0, bytes.Length); - zipStream.Dispose(); - - return ms.ToArray(); - } - } - - private byte[] GZip(byte[] buffer) - { - using (var ms = new MemoryStream()) - using (var zipStream = new GZipStream(ms, CompressionMode.Compress)) - { - zipStream.Write(buffer, 0, buffer.Length); - zipStream.Dispose(); - - return ms.ToArray(); + return new StreamWriter(stream, contentType, _logger) + { + OnComplete = options.OnComplete, + OnError = options.OnError + }; } } diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index a6ed84f29f..a277b693aa 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -269,7 +269,41 @@ namespace Emby.Server.Implementations.Library return new List(); } - var excludeItemTypes = includeItemTypes.Length == 0 ? new[] + var mediaTypes = new List(); + + if (includeItemTypes.Length == 0) + { + foreach (var parent in parents.OfType()) + { + switch (parent.CollectionType) + { + case CollectionType.Books: + mediaTypes.Add(MediaType.Book); + break; + case CollectionType.Games: + mediaTypes.Add(MediaType.Game); + break; + case CollectionType.Music: + mediaTypes.Add(MediaType.Audio); + break; + case CollectionType.Photos: + mediaTypes.Add(MediaType.Photo); + mediaTypes.Add(MediaType.Video); + break; + case CollectionType.HomeVideos: + mediaTypes.Add(MediaType.Photo); + mediaTypes.Add(MediaType.Video); + break; + default: + mediaTypes.Add(MediaType.Video); + break; + } + } + + mediaTypes = mediaTypes.Distinct().ToList(); + } + + var excludeItemTypes = includeItemTypes.Length == 0 && mediaTypes.Count == 0 ? new[] { typeof(Person).Name, typeof(Studio).Name, @@ -290,7 +324,8 @@ namespace Emby.Server.Implementations.Library IsVirtualItem = false, Limit = limit * 5, IsPlayed = isPlayed, - DtoOptions = options + DtoOptions = options, + MediaTypes = mediaTypes.ToArray() }; if (parents.Count == 0) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 9ac599846b..99b5558a2d 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1632,7 +1632,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return; } - var episodesToDelete = (librarySeries.GetItems(new InternalItemsQuery + var episodesToDelete = (librarySeries.GetItemList(new InternalItemsQuery { SortBy = new[] { ItemSortBy.DateCreated }, SortOrder = SortOrder.Descending, @@ -1642,7 +1642,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV DtoOptions = new DtoOptions(true) })) - .Items .Where(i => i.LocationType == LocationType.FileSystem && _fileSystem.FileExists(i.Path)) .Skip(seriesTimer.KeepUpTo - 1) .ToList(); diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 1f9817e201..10aab4054a 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1845,6 +1845,9 @@ namespace Emby.Server.Implementations.LiveTv public async Task AddInfoToProgramDto(List> tuples, List fields, User user = null) { var programTuples = new List>(); + var hasChannelImage = fields.Contains(ItemFields.ChannelImage); + var hasChannelInfo = fields.Contains(ItemFields.ChannelInfo); + var hasServiceName = fields.Contains(ItemFields.ServiceName); foreach (var tuple in tuples) { @@ -1887,7 +1890,7 @@ namespace Emby.Server.Implementations.LiveTv dto.IsPremiere = program.IsPremiere; } - if (fields.Contains(ItemFields.ChannelInfo)) + if (hasChannelInfo || hasChannelImage) { var channel = GetInternalChannel(program.ChannelId); @@ -1897,7 +1900,7 @@ namespace Emby.Server.Implementations.LiveTv dto.MediaType = channel.MediaType; dto.ChannelNumber = channel.Number; - if (channel.HasImage(ImageType.Primary)) + if (hasChannelImage && channel.HasImage(ImageType.Primary)) { dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel); } @@ -1906,7 +1909,7 @@ namespace Emby.Server.Implementations.LiveTv var serviceName = program.ServiceName; - if (fields.Contains(ItemFields.ServiceName)) + if (hasServiceName) { dto.ServiceName = serviceName; } diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 317f40a37a..763ca9f24c 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1089,7 +1089,7 @@ namespace Emby.Server.Implementations.Session { var folder = (Folder)item; - var itemsResult = folder.GetItems(new InternalItemsQuery(user) + var itemsResult = folder.GetItemList(new InternalItemsQuery(user) { Recursive = true, IsFolder = false, @@ -1104,7 +1104,7 @@ namespace Emby.Server.Implementations.Session }); - return FilterToSingleMediaType(itemsResult.Items) + return FilterToSingleMediaType(itemsResult) .OrderBy(i => i.SortName) .ToList(); } diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index 876c5d58b0..03283031eb 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -42,7 +42,7 @@ namespace Emby.Server.Implementations.TV int? limit = null; if (!string.IsNullOrWhiteSpace(request.SeriesId)) { - var series = _libraryManager.GetItemById(request.SeriesId); + var series = _libraryManager.GetItemById(request.SeriesId) as Series; if (series != null) { @@ -51,17 +51,22 @@ namespace Emby.Server.Implementations.TV } } - if (string.IsNullOrWhiteSpace(presentationUniqueKey) && limit.HasValue) + if (!string.IsNullOrWhiteSpace(presentationUniqueKey)) + { + return GetResult(GetNextUpEpisodes(request, user, new[] { presentationUniqueKey }, dtoOptions), request); + } + + if (limit.HasValue) { limit = limit.Value + 10; } var items = _libraryManager.GetItemList(new InternalItemsQuery(user) { - IncludeItemTypes = new[] { typeof(Series).Name }, - SortBy = new[] { ItemSortBy.SeriesDatePlayed }, + IncludeItemTypes = new[] { typeof(Episode).Name }, + SortBy = new[] { ItemSortBy.DatePlayed }, SortOrder = SortOrder.Descending, - PresentationUniqueKey = presentationUniqueKey, + SeriesPresentationUniqueKey = presentationUniqueKey, Limit = limit, ParentId = parentIdGuid, Recursive = true, @@ -69,11 +74,12 @@ namespace Emby.Server.Implementations.TV { Fields = new List { - ItemFields.PresentationUniqueKey + ItemFields.SeriesPresentationUniqueKey } - } + }, + GroupBySeriesPresentationUniqueKey = true - }).Cast().Select(GetUniqueSeriesKey); + }).Cast().Select(GetUniqueSeriesKey); // Avoid implicitly captured closure var episodes = GetNextUpEpisodes(request, user, items, dtoOptions); @@ -94,7 +100,7 @@ namespace Emby.Server.Implementations.TV int? limit = null; if (!string.IsNullOrWhiteSpace(request.SeriesId)) { - var series = _libraryManager.GetItemById(request.SeriesId); + var series = _libraryManager.GetItemById(request.SeriesId) as Series; if (series != null) { @@ -103,28 +109,34 @@ namespace Emby.Server.Implementations.TV } } - if (string.IsNullOrWhiteSpace(presentationUniqueKey) && limit.HasValue) + if (!string.IsNullOrWhiteSpace(presentationUniqueKey)) + { + return GetResult(GetNextUpEpisodes(request, user, new [] { presentationUniqueKey }, dtoOptions), request); + } + + if (limit.HasValue) { limit = limit.Value + 10; } var items = _libraryManager.GetItemList(new InternalItemsQuery(user) { - IncludeItemTypes = new[] { typeof(Series).Name }, - SortBy = new[] { ItemSortBy.SeriesDatePlayed }, + IncludeItemTypes = new[] { typeof(Episode).Name }, + SortBy = new[] { ItemSortBy.DatePlayed }, SortOrder = SortOrder.Descending, - PresentationUniqueKey = presentationUniqueKey, + SeriesPresentationUniqueKey = presentationUniqueKey, Limit = limit, DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions { Fields = new List { - ItemFields.PresentationUniqueKey + ItemFields.SeriesPresentationUniqueKey }, EnableImages = false - } + }, + GroupBySeriesPresentationUniqueKey = true - }, parentsFolders.Cast().ToList()).Cast().Select(GetUniqueSeriesKey); + }, parentsFolders.Cast().ToList()).Cast().Select(GetUniqueSeriesKey); // Avoid implicitly captured closure var episodes = GetNextUpEpisodes(request, user, items, dtoOptions); @@ -167,7 +179,12 @@ namespace Emby.Server.Implementations.TV .Where(i => i != null); } - private string GetUniqueSeriesKey(BaseItem series) + private string GetUniqueSeriesKey(Episode episode) + { + return episode.SeriesPresentationUniqueKey; + } + + private string GetUniqueSeriesKey(Series series) { return series.GetPresentationUniqueKey(); } diff --git a/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs b/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs index f546133847..863391eea1 100644 --- a/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs +++ b/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs @@ -42,7 +42,7 @@ namespace Emby.Server.Implementations.UserViews var recursive = !new[] { CollectionType.Playlists, CollectionType.Channels }.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase); - var result = view.GetItems(new InternalItemsQuery + var result = view.GetItemList(new InternalItemsQuery { CollapseBoxSetItems = false, Recursive = recursive, @@ -51,7 +51,7 @@ namespace Emby.Server.Implementations.UserViews }); - var items = result.Items.Select(i => + var items = result.Select(i => { var episode = i as Episode; if (episode != null) diff --git a/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs b/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs index cd2c4728f1..5230da8a66 100644 --- a/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs +++ b/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs @@ -70,19 +70,19 @@ namespace Emby.Server.Implementations.UserViews if (string.Equals(view.ViewType, SpecialFolder.MovieGenre, StringComparison.OrdinalIgnoreCase) || string.Equals(view.ViewType, SpecialFolder.TvGenre, StringComparison.OrdinalIgnoreCase)) { - var userItemsResult = view.GetItems(new InternalItemsQuery + var userItemsResult = view.GetItemList(new InternalItemsQuery { CollapseBoxSetItems = false, DtoOptions = new DtoOptions(false) }); - return userItemsResult.Items.ToList(); + return userItemsResult.ToList(); } var isUsingCollectionStrip = IsUsingCollectionStrip(view); var recursive = isUsingCollectionStrip && !new[] { CollectionType.Channels, CollectionType.BoxSets, CollectionType.Playlists }.Contains(view.ViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase); - var result = view.GetItems(new InternalItemsQuery + var result = view.GetItemList(new InternalItemsQuery { User = view.UserId.HasValue ? _userManager.GetUserById(view.UserId.Value) : null, CollapseBoxSetItems = false, @@ -91,7 +91,7 @@ namespace Emby.Server.Implementations.UserViews DtoOptions = new DtoOptions(false) }); - var items = result.Items.Select(i => + var items = result.Select(i => { var episode = i as Episode; if (episode != null) diff --git a/MediaBrowser.Api/FilterService.cs b/MediaBrowser.Api/FilterService.cs index a1f891506a..5d81e9ea56 100644 --- a/MediaBrowser.Api/FilterService.cs +++ b/MediaBrowser.Api/FilterService.cs @@ -61,9 +61,9 @@ namespace MediaBrowser.Api user == null ? _libraryManager.RootFolder : user.RootFolder : parentItem; - var result = ((Folder)item).GetItems(GetItemsQuery(request, user)); + var result = ((Folder)item).GetItemList(GetItemsQuery(request, user)); - return ToOptimizedResult(GetFilters(result.Items)); + return ToOptimizedResult(GetFilters(result.ToArray())); } private QueryFilters GetFilters(BaseItem[] items) diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index 95366c79e6..3f00f8ecf7 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -438,14 +438,14 @@ namespace MediaBrowser.Api throw new ResourceNotFoundException("Series not found"); } - var seasons = (series.GetItems(new InternalItemsQuery(user) + var seasons = (series.GetItemList(new InternalItemsQuery(user) { IsMissing = request.IsMissing, IsVirtualUnaired = request.IsVirtualUnaired, IsSpecialSeason = request.IsSpecialSeason, AdjacentTo = request.AdjacentTo - })).Items.OfType(); + })).OfType(); var dtoOptions = GetDtoOptions(_authContext, request); diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 5d74cf218b..a3f097f245 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -970,6 +970,27 @@ namespace MediaBrowser.Controller.Entities return GetItemsInternal(query); } + public IEnumerable GetItemList(InternalItemsQuery query) + { + query.EnableTotalRecordCount = false; + + if (query.ItemIds.Length > 0) + { + var result = LibraryManager.GetItemList(query); + + if (query.SortBy.Length == 0) + { + var ids = query.ItemIds.ToList(); + + // Try to preserve order + result = result.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToArray(); + } + return result; + } + + return GetItemsInternal(query).Items; + } + protected virtual QueryResult GetItemsInternal(InternalItemsQuery query) { if (SourceType == SourceType.Channel) @@ -1375,10 +1396,10 @@ namespace MediaBrowser.Controller.Entities query.IsVirtualItem = false; } - var itemsResult = GetItems(query); + var itemsResult = GetItemList(query); // Sweep through recursively and update status - var tasks = itemsResult.Items.Select(c => c.MarkPlayed(user, datePlayed, resetPosition)); + var tasks = itemsResult.Select(c => c.MarkPlayed(user, datePlayed, resetPosition)); await Task.WhenAll(tasks).ConfigureAwait(false); } @@ -1390,7 +1411,7 @@ namespace MediaBrowser.Controller.Entities /// Task. public override async Task MarkUnplayed(User user) { - var itemsResult = GetItems(new InternalItemsQuery + var itemsResult = GetItemList(new InternalItemsQuery { User = user, Recursive = true, @@ -1400,14 +1421,14 @@ namespace MediaBrowser.Controller.Entities }); // Sweep through recursively and update status - var tasks = itemsResult.Items.Select(c => c.MarkUnplayed(user)); + var tasks = itemsResult.Select(c => c.MarkUnplayed(user)); await Task.WhenAll(tasks).ConfigureAwait(false); } public override bool IsPlayed(User user) { - var itemsResult = GetItems(new InternalItemsQuery(user) + var itemsResult = GetItemList(new InternalItemsQuery(user) { Recursive = true, IsFolder = false, @@ -1416,7 +1437,7 @@ namespace MediaBrowser.Controller.Entities }); - return itemsResult.Items + return itemsResult .All(i => i.IsPlayed(user)); } diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 4f21aaa56b..d9c8223c16 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -161,6 +161,7 @@ namespace MediaBrowser.Controller.Entities public string SeriesPresentationUniqueKey { get; set; } public bool GroupByPresentationUniqueKey { get; set; } + public bool GroupBySeriesPresentationUniqueKey { get; set; } public bool EnableTotalRecordCount { get; set; } public bool ForceDirect { get; set; } public Dictionary ExcludeProviderIds { get; set; } diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 8b73b80b02..229e63f139 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -193,7 +193,7 @@ namespace MediaBrowser.Controller.Entities.TV if (query.IncludeItemTypes.Length == 0) { - query.IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name }; + query.IncludeItemTypes = new[] { typeof(Episode).Name }; } query.IsVirtualItem = false; query.Limit = 0; diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index 0d2d69c949..4c44a613b1 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -82,7 +82,7 @@ namespace MediaBrowser.Controller.Entities public override IEnumerable GetChildren(User user, bool includeLinkedChildren) { - var result = GetItems(new InternalItemsQuery + var result = GetItemList(new InternalItemsQuery { User = user, EnableTotalRecordCount = false, @@ -90,7 +90,7 @@ namespace MediaBrowser.Controller.Entities }); - return result.Items; + return result; } public override bool CanDelete() @@ -105,7 +105,7 @@ namespace MediaBrowser.Controller.Entities public override IEnumerable GetRecursiveChildren(User user, InternalItemsQuery query) { - var result = GetItems(new InternalItemsQuery + var result = GetItemList(new InternalItemsQuery { User = user, Recursive = true, @@ -117,7 +117,7 @@ namespace MediaBrowser.Controller.Entities }); - return result.Items.Where(i => UserViewBuilder.FilterItem(i, query)); + return result.Where(i => UserViewBuilder.FilterItem(i, query)); } protected override IEnumerable GetEligibleChildrenForRecursiveChildren(User user) diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index c992ac56a7..eaadc68717 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -182,10 +182,7 @@ namespace MediaBrowser.Controller.Playlists DtoOptions = options }; - var itemsResult = folder.GetItems(query); - var items = itemsResult.Items; - - return items; + return folder.GetItemList(query); } return new[] { item }; diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs index e088771b53..a294e4a3a8 100644 --- a/MediaBrowser.Model/Querying/ItemFields.cs +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -224,6 +224,7 @@ SeriesPresentationUniqueKey, DateLastRefreshed, DateLastSaved, - RefreshState + RefreshState, + ChannelImage } } diff --git a/MediaBrowser.Server.Mono/Program.cs b/MediaBrowser.Server.Mono/Program.cs index 0a70c446f7..2d5bf85e50 100644 --- a/MediaBrowser.Server.Mono/Program.cs +++ b/MediaBrowser.Server.Mono/Program.cs @@ -97,7 +97,7 @@ namespace MediaBrowser.Server.Mono private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, StartupOptions options) { // Allow all https requests - ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }); + //ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }); var environmentInfo = GetEnvironmentInfo(); -- cgit v1.2.3 From db315c4e3279a0f7c5fd5507887b32c6436b90af Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 5 Aug 2017 15:02:33 -0400 Subject: update media source methods --- Emby.Dlna/Didl/DidlBuilder.cs | 4 +- Emby.Dlna/PlayTo/PlayToController.cs | 2 +- .../Data/SqliteItemRepository.cs | 25 +--- .../Data/SqliteUserDataRepository.cs | 2 +- Emby.Server.Implementations/Dto/DtoService.cs | 4 +- .../Emby.Server.Implementations.csproj | 3 + .../Library/MediaSourceManager.cs | 15 ++- .../LiveTv/LiveTvManager.cs | 4 +- .../LiveTv/LiveTvMediaSourceProvider.cs | 3 +- .../Localization/Ratings/es.txt | 1 + .../Localization/Ratings/ro.txt | 1 + .../MediaEncoder/EncodingManager.cs | 22 ++-- MediaBrowser.Controller/Entities/Audio/Audio.cs | 22 +++- MediaBrowser.Controller/Entities/IHasId.cs | 9 -- MediaBrowser.Controller/Entities/IHasImages.cs | 2 +- .../Entities/IHasMediaSources.cs | 4 +- MediaBrowser.Controller/Entities/IHasMetadata.cs | 1 + MediaBrowser.Controller/Entities/IHasUserData.cs | 7 +- MediaBrowser.Controller/Entities/Video.cs | 66 +++++++--- .../Library/IMediaSourceManager.cs | 8 +- .../LiveTv/LiveTvAudioRecording.cs | 4 +- MediaBrowser.Controller/LiveTv/LiveTvChannel.cs | 7 +- .../LiveTv/LiveTvVideoRecording.cs | 4 +- .../MediaBrowser.Controller.csproj | 1 - .../MediaEncoding/EncodingHelper.cs | 28 ++++- .../Persistence/IItemRepository.cs | 2 +- MediaBrowser.Model/Dto/MediaSourceInfo.cs | 2 - .../MediaInfo/AudioImageProvider.cs | 4 +- .../MediaInfo/FFProbeVideoInfo.cs | 78 +++++------- .../MediaInfo/VideoImageProvider.cs | 138 +++++++-------------- MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs | 9 +- 31 files changed, 235 insertions(+), 247 deletions(-) create mode 100644 Emby.Server.Implementations/Localization/Ratings/ro.txt delete mode 100644 MediaBrowser.Controller/Entities/IHasId.cs (limited to 'Emby.Server.Implementations/Data/SqliteItemRepository.cs') diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index a1b692d98b..71a0493948 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -193,7 +193,7 @@ namespace Emby.Dlna.Didl { if (streamInfo == null) { - var sources = _mediaSourceManager.GetStaticMediaSources(video, true, _user).ToList(); + var sources = _mediaSourceManager.GetStaticMediaSources(video, true, _user); streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildVideoItem(new VideoOptions { @@ -508,7 +508,7 @@ namespace Emby.Dlna.Didl if (streamInfo == null) { - var sources = _mediaSourceManager.GetStaticMediaSources(audio, true, _user).ToList(); + var sources = _mediaSourceManager.GetStaticMediaSources(audio, true, _user); streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildAudioItem(new AudioOptions { diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index 15d73e824b..7164cf5989 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -503,7 +503,7 @@ namespace Emby.Dlna.PlayTo var hasMediaSources = item as IHasMediaSources; var mediaSources = hasMediaSources != null - ? (_mediaSourceManager.GetStaticMediaSources(hasMediaSources, true, user)).ToList() + ? (_mediaSourceManager.GetStaticMediaSources(hasMediaSources, true, user)) : new List(); var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex); diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 80a5defd67..3825389bfa 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -113,7 +113,7 @@ namespace Emby.Server.Implementations.Data { get { - return true; + return false; } } @@ -1972,20 +1972,7 @@ namespace Emby.Server.Implementations.Data /// Task{IEnumerable{ItemReview}}. public IEnumerable GetCriticReviews(Guid itemId) { - try - { - var path = Path.Combine(_criticReviewsPath, itemId + ".json"); - - return _jsonSerializer.DeserializeFromFile>(path); - } - catch (FileNotFoundException) - { - return new List(); - } - catch (IOException) - { - return new List(); - } + return new List(); } private readonly Task _cachedTask = Task.FromResult(true); @@ -1997,12 +1984,6 @@ namespace Emby.Server.Implementations.Data /// Task. public Task SaveCriticReviews(Guid itemId, IEnumerable criticReviews) { - _fileSystem.CreateDirectory(_criticReviewsPath); - - var path = Path.Combine(_criticReviewsPath, itemId + ".json"); - - _jsonSerializer.SerializeToFile(criticReviews.ToList(), path); - return _cachedTask; } @@ -5610,7 +5591,7 @@ namespace Emby.Server.Implementations.Data return item; } - public IEnumerable GetMediaStreams(MediaStreamQuery query) + public List GetMediaStreams(MediaStreamQuery query) { CheckDisposed(); diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index bf6388f5d8..b76555bde5 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -95,7 +95,7 @@ namespace Emby.Server.Implementations.Data { get { - return true; + return false; } } diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index a57ee0b9b0..c50e5600a2 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -359,11 +359,11 @@ namespace Emby.Server.Implementations.Dto { if (user == null) { - dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true).ToList(); + dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true); } else { - dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true, user).ToList(); + dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true, user); } } } diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 3d6e02816e..ed69c9755b 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -413,6 +413,9 @@ + + + - \ No newline at end of file diff --git a/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs deleted file mode 100644 index 2c7e6a4870..0000000000 --- a/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs +++ /dev/null @@ -1,334 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Net; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Plugins; -using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.Events; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Threading; -using Mono.Nat; -using System.Threading.Tasks; - -namespace Emby.Server.Core.EntryPoints -{ - public class ExternalPortForwarding : IServerEntryPoint - { - private readonly IServerApplicationHost _appHost; - private readonly ILogger _logger; - private readonly IHttpClient _httpClient; - private readonly IServerConfigurationManager _config; - private readonly IDeviceDiscovery _deviceDiscovery; - - private ITimer _timer; - private bool _isStarted; - private readonly ITimerFactory _timerFactory; - - public ExternalPortForwarding(ILogManager logmanager, IServerApplicationHost appHost, IServerConfigurationManager config, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, ITimerFactory timerFactory) - { - _logger = logmanager.GetLogger("PortMapper"); - _appHost = appHost; - _config = config; - _deviceDiscovery = deviceDiscovery; - _httpClient = httpClient; - _timerFactory = timerFactory; - } - - private string _lastConfigIdentifier; - private string GetConfigIdentifier() - { - var values = new List(); - var config = _config.Configuration; - - values.Add(config.EnableUPnP.ToString()); - values.Add(config.PublicPort.ToString(CultureInfo.InvariantCulture)); - values.Add(_appHost.HttpPort.ToString(CultureInfo.InvariantCulture)); - values.Add(_appHost.HttpsPort.ToString(CultureInfo.InvariantCulture)); - values.Add(config.EnableHttps.ToString()); - values.Add(_appHost.EnableHttps.ToString()); - - return string.Join("|", values.ToArray()); - } - - void _config_ConfigurationUpdated(object sender, EventArgs e) - { - if (!string.Equals(_lastConfigIdentifier, GetConfigIdentifier(), StringComparison.OrdinalIgnoreCase)) - { - if (_isStarted) - { - DisposeNat(); - } - - Run(); - } - } - - public void Run() - { - NatUtility.Logger = _logger; - NatUtility.HttpClient = _httpClient; - - if (_config.Configuration.EnableUPnP) - { - Start(); - } - - _config.ConfigurationUpdated -= _config_ConfigurationUpdated; - _config.ConfigurationUpdated += _config_ConfigurationUpdated; - } - - private void Start() - { - _logger.Debug("Starting NAT discovery"); - NatUtility.EnabledProtocols = new List - { - NatProtocol.Pmp - }; - NatUtility.DeviceFound += NatUtility_DeviceFound; - - // Mono.Nat does never rise this event. The event is there however it is useless. - // You could remove it with no risk. - NatUtility.DeviceLost += NatUtility_DeviceLost; - - - NatUtility.StartDiscovery(); - - _timer = _timerFactory.Create(ClearCreatedRules, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10)); - - _deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered; - - _lastConfigIdentifier = GetConfigIdentifier(); - - _isStarted = true; - } - - private async void _deviceDiscovery_DeviceDiscovered(object sender, GenericEventArgs e) - { - if (_disposed) - { - return; - } - - var info = e.Argument; - - string usn; - if (!info.Headers.TryGetValue("USN", out usn)) usn = string.Empty; - - string nt; - if (!info.Headers.TryGetValue("NT", out nt)) nt = string.Empty; - - // Filter device type - if (usn.IndexOf("WANIPConnection:", StringComparison.OrdinalIgnoreCase) == -1 && - nt.IndexOf("WANIPConnection:", StringComparison.OrdinalIgnoreCase) == -1 && - usn.IndexOf("WANPPPConnection:", StringComparison.OrdinalIgnoreCase) == -1 && - nt.IndexOf("WANPPPConnection:", StringComparison.OrdinalIgnoreCase) == -1) - { - return; - } - - var identifier = string.IsNullOrWhiteSpace(usn) ? nt : usn; - - if (info.Location == null) - { - return; - } - - lock (_usnsHandled) - { - if (_usnsHandled.Contains(identifier)) - { - return; - } - _usnsHandled.Add(identifier); - } - - _logger.Debug("Found NAT device: " + identifier); - - IPAddress address; - if (IPAddress.TryParse(info.Location.Host, out address)) - { - // The Handle method doesn't need the port - var endpoint = new IPEndPoint(address, info.Location.Port); - - IPAddress localAddress = null; - - try - { - var localAddressString = await _appHost.GetLocalApiUrl().ConfigureAwait(false); - - Uri uri; - if (Uri.TryCreate(localAddressString, UriKind.Absolute, out uri)) - { - localAddressString = uri.Host; - - if (!IPAddress.TryParse(localAddressString, out localAddress)) - { - return; - } - } - } - catch (Exception ex) - { - return; - } - - if (_disposed) - { - return; - } - - _logger.Debug("Calling Nat.Handle on " + identifier); - NatUtility.Handle(localAddress, info, endpoint, NatProtocol.Upnp); - } - } - - private void ClearCreatedRules(object state) - { - lock (_createdRules) - { - _createdRules.Clear(); - } - lock (_usnsHandled) - { - _usnsHandled.Clear(); - } - } - - void NatUtility_DeviceFound(object sender, DeviceEventArgs e) - { - if (_disposed) - { - return; - } - - try - { - var device = e.Device; - _logger.Debug("NAT device found: {0}", device.LocalAddress.ToString()); - - CreateRules(device); - } - catch - { - // I think it could be a good idea to log the exception because - // you are using permanent portmapping here (never expire) and that means that next time - // CreatePortMap is invoked it can fails with a 718-ConflictInMappingEntry or not. That depends - // on the router's upnp implementation (specs says it should fail however some routers don't do it) - // It also can fail with others like 727-ExternalPortOnlySupportsWildcard, 728-NoPortMapsAvailable - // and those errors (upnp errors) could be useful for diagnosting. - - // Commenting out because users are reporting problems out of our control - //_logger.ErrorException("Error creating port forwarding rules", ex); - } - } - - private List _createdRules = new List(); - private List _usnsHandled = new List(); - private async void CreateRules(INatDevice device) - { - if (_disposed) - { - throw new ObjectDisposedException("PortMapper"); - } - - // 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 - - var address = device.LocalAddress.ToString(); - - lock (_createdRules) - { - if (!_createdRules.Contains(address)) - { - _createdRules.Add(address); - } - else - { - return; - } - } - - var success = await CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort).ConfigureAwait(false); - - if (success) - { - await CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort).ConfigureAwait(false); - } - } - - private async Task CreatePortMap(INatDevice device, int privatePort, int publicPort) - { - _logger.Debug("Creating port map on port {0}", privatePort); - - try - { - await device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort) - { - Description = _appHost.Name - - }).ConfigureAwait(false); - - return true; - } - catch (Exception ex) - { - _logger.Error("Error creating port map: " + ex.Message); - - return false; - } - } - - // As I said before, this method will be never invoked. You can remove it. - void NatUtility_DeviceLost(object sender, DeviceEventArgs e) - { - var device = e.Device; - _logger.Debug("NAT device lost: {0}", device.LocalAddress.ToString()); - } - - private bool _disposed = false; - public void Dispose() - { - _disposed = true; - DisposeNat(); - } - - private void DisposeNat() - { - _logger.Debug("Stopping NAT discovery"); - - if (_timer != null) - { - _timer.Dispose(); - _timer = null; - } - - _deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered; - - try - { - // This is not a significant improvement - NatUtility.StopDiscovery(); - NatUtility.DeviceFound -= NatUtility_DeviceFound; - NatUtility.DeviceLost -= NatUtility_DeviceLost; - } - // Statements in try-block will no fail because StopDiscovery is a one-line - // method that was no chances to fail. - // public static void StopDiscovery () - // { - // searching.Reset(); - // } - // IMO you could remove the catch-block - catch (Exception ex) - { - _logger.ErrorException("Error stopping NAT Discovery", ex); - } - finally - { - _isStarted = false; - } - } - } -} \ No newline at end of file diff --git a/Emby.Server.Core/HttpServerFactory.cs b/Emby.Server.Core/HttpServerFactory.cs deleted file mode 100644 index e16cbea0e3..0000000000 --- a/Emby.Server.Core/HttpServerFactory.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System; -using System.IO; -using System.Net.Security; -using System.Net.Sockets; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using System.Threading.Tasks; -using Emby.Common.Implementations.Net; -using Emby.Server.Implementations.HttpServer; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Cryptography; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.System; -using MediaBrowser.Model.Text; -using ServiceStack.Text.Jsv; -using SocketHttpListener.Primitives; - -namespace Emby.Server.Core -{ - /// - /// Class ServerFactory - /// - public static class HttpServerFactory - { - /// - /// Creates the server. - /// - /// IHttpServer. - public static IHttpServer CreateServer(IServerApplicationHost applicationHost, - ILogManager logManager, - IServerConfigurationManager config, - INetworkManager networkmanager, - IMemoryStreamFactory streamProvider, - string serverName, - string defaultRedirectpath, - ITextEncoding textEncoding, - ISocketFactory socketFactory, - ICryptoProvider cryptoProvider, - IJsonSerializer json, - IXmlSerializer xml, - IEnvironmentInfo environment, - ICertificate certificate, - IFileSystem fileSystem, - bool enableDualModeSockets) - { - var logger = logManager.GetLogger("HttpServer"); - - return new HttpListenerHost(applicationHost, - logger, - config, - serverName, - defaultRedirectpath, - networkmanager, - streamProvider, - textEncoding, - socketFactory, - cryptoProvider, - json, - xml, - environment, - certificate, - new StreamFactory(), - GetParseFn, - enableDualModeSockets, - fileSystem); - } - - private static Func GetParseFn(Type propertyType) - { - return s => JsvReader.GetParseFn(propertyType)(s); - } - } - - public class StreamFactory : IStreamFactory - { - public Stream CreateNetworkStream(IAcceptSocket acceptSocket, bool ownsSocket) - { - var netSocket = (NetAcceptSocket)acceptSocket; - - return new SocketStream(netSocket.Socket, ownsSocket); - } - - public Task AuthenticateSslStreamAsServer(Stream stream, ICertificate certificate) - { - var sslStream = (SslStream)stream; - var cert = (Certificate)certificate; - - return sslStream.AuthenticateAsServerAsync(cert.X509Certificate); - } - - public Stream CreateSslStream(Stream innerStream, bool leaveInnerStreamOpen) - { - return new SslStream(innerStream, leaveInnerStreamOpen); - } - } - - public class Certificate : ICertificate - { - public Certificate(X509Certificate x509Certificate) - { - X509Certificate = x509Certificate; - } - - public X509Certificate X509Certificate { get; private set; } - } -} diff --git a/Emby.Server.Core/IO/LibraryMonitor.cs b/Emby.Server.Core/IO/LibraryMonitor.cs deleted file mode 100644 index ebc5e5e558..0000000000 --- a/Emby.Server.Core/IO/LibraryMonitor.cs +++ /dev/null @@ -1,653 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Plugins; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.System; -using MediaBrowser.Model.Tasks; -using MediaBrowser.Model.Threading; -using Emby.Server.Implementations.IO; - -namespace Emby.Server.Core.IO -{ - public class LibraryMonitor : ILibraryMonitor - { - /// - /// The file system watchers - /// - private readonly ConcurrentDictionary _fileSystemWatchers = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - /// - /// The affected paths - /// - private readonly List _activeRefreshers = new List(); - - /// - /// A dynamic list of paths that should be ignored. Added to during our own file sytem modifications. - /// - private readonly ConcurrentDictionary _tempIgnoredPaths = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - - /// - /// Any file name ending in any of these will be ignored by the watchers - /// - private readonly IReadOnlyList _alwaysIgnoreFiles = new List - { - "small.jpg", - "albumart.jpg", - - // WMC temp recording directories that will constantly be written to - "TempRec", - "TempSBE" - }; - - private readonly IReadOnlyList _alwaysIgnoreSubstrings = new List - { - // Synology - "eaDir", - "#recycle", - ".wd_tv", - ".actors" - }; - - private readonly IReadOnlyList _alwaysIgnoreExtensions = new List - { - // thumbs.db - ".db", - - // bts sync files - ".bts", - ".sync" - }; - - /// - /// Add the path to our temporary ignore list. Use when writing to a path within our listening scope. - /// - /// The path. - private void TemporarilyIgnore(string path) - { - _tempIgnoredPaths[path] = path; - } - - public void ReportFileSystemChangeBeginning(string path) - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException("path"); - } - - TemporarilyIgnore(path); - } - - public bool IsPathLocked(string path) - { - var lockedPaths = _tempIgnoredPaths.Keys.ToList(); - return lockedPaths.Any(i => _fileSystem.AreEqual(i, path) || _fileSystem.ContainsSubPath(i, path)); - } - - public async void ReportFileSystemChangeComplete(string path, bool refreshPath) - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException("path"); - } - - // This is an arbitraty amount of time, but delay it because file system writes often trigger events long after the file was actually written to. - // Seeing long delays in some situations, especially over the network, sometimes up to 45 seconds - // But if we make this delay too high, we risk missing legitimate changes, such as user adding a new file, or hand-editing metadata - await Task.Delay(45000).ConfigureAwait(false); - - string val; - _tempIgnoredPaths.TryRemove(path, out val); - - if (refreshPath) - { - try - { - ReportFileSystemChanged(path); - } - catch (Exception ex) - { - Logger.ErrorException("Error in ReportFileSystemChanged for {0}", ex, path); - } - } - } - - /// - /// Gets or sets the logger. - /// - /// The logger. - private ILogger Logger { get; set; } - - /// - /// Gets or sets the task manager. - /// - /// The task manager. - private ITaskManager TaskManager { get; set; } - - private ILibraryManager LibraryManager { get; set; } - private IServerConfigurationManager ConfigurationManager { get; set; } - - private readonly IFileSystem _fileSystem; - private readonly ITimerFactory _timerFactory; - private readonly IEnvironmentInfo _environmentInfo; - - /// - /// Initializes a new instance of the class. - /// - public LibraryMonitor(ILogManager logManager, ITaskManager taskManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ITimerFactory timerFactory, ISystemEvents systemEvents, IEnvironmentInfo environmentInfo) - { - if (taskManager == null) - { - throw new ArgumentNullException("taskManager"); - } - - LibraryManager = libraryManager; - TaskManager = taskManager; - Logger = logManager.GetLogger(GetType().Name); - ConfigurationManager = configurationManager; - _fileSystem = fileSystem; - _timerFactory = timerFactory; - _environmentInfo = environmentInfo; - - systemEvents.Resume += _systemEvents_Resume; - } - - private void _systemEvents_Resume(object sender, EventArgs e) - { - Restart(); - } - - private void Restart() - { - Stop(); - - if (!_disposed) - { - Start(); - } - } - - private bool IsLibraryMonitorEnabaled(BaseItem item) - { - if (item is BasePluginFolder) - { - return false; - } - - var options = LibraryManager.GetLibraryOptions(item); - - if (options != null) - { - return options.EnableRealtimeMonitor; - } - - return false; - } - - public void Start() - { - LibraryManager.ItemAdded += LibraryManager_ItemAdded; - LibraryManager.ItemRemoved += LibraryManager_ItemRemoved; - - var pathsToWatch = new List { }; - - var paths = LibraryManager - .RootFolder - .Children - .Where(IsLibraryMonitorEnabaled) - .OfType() - .SelectMany(f => f.PhysicalLocations) - .Distinct(StringComparer.OrdinalIgnoreCase) - .OrderBy(i => i) - .ToList(); - - foreach (var path in paths) - { - if (!ContainsParentFolder(pathsToWatch, path)) - { - pathsToWatch.Add(path); - } - } - - foreach (var path in pathsToWatch) - { - StartWatchingPath(path); - } - } - - private void StartWatching(BaseItem item) - { - if (IsLibraryMonitorEnabaled(item)) - { - StartWatchingPath(item.Path); - } - } - - /// - /// Handles the ItemRemoved event of the LibraryManager control. - /// - /// The source of the event. - /// The instance containing the event data. - void LibraryManager_ItemRemoved(object sender, ItemChangeEventArgs e) - { - if (e.Item.GetParent() is AggregateFolder) - { - StopWatchingPath(e.Item.Path); - } - } - - /// - /// Handles the ItemAdded event of the LibraryManager control. - /// - /// The source of the event. - /// The instance containing the event data. - void LibraryManager_ItemAdded(object sender, ItemChangeEventArgs e) - { - if (e.Item.GetParent() is AggregateFolder) - { - StartWatching(e.Item); - } - } - - /// - /// Examine a list of strings assumed to be file paths to see if it contains a parent of - /// the provided path. - /// - /// The LST. - /// The path. - /// true if [contains parent folder] [the specified LST]; otherwise, false. - /// path - private static bool ContainsParentFolder(IEnumerable lst, string path) - { - if (string.IsNullOrWhiteSpace(path)) - { - throw new ArgumentNullException("path"); - } - - path = path.TrimEnd(Path.DirectorySeparatorChar); - - return lst.Any(str => - { - //this should be a little quicker than examining each actual parent folder... - var compare = str.TrimEnd(Path.DirectorySeparatorChar); - - return path.Equals(compare, StringComparison.OrdinalIgnoreCase) || (path.StartsWith(compare, StringComparison.OrdinalIgnoreCase) && path[compare.Length] == Path.DirectorySeparatorChar); - }); - } - - /// - /// Starts the watching path. - /// - /// The path. - private void StartWatchingPath(string path) - { - if (!_fileSystem.DirectoryExists(path)) - { - // Seeing a crash in the mono runtime due to an exception being thrown on a different thread - Logger.Info("Skipping realtime monitor for {0} because the path does not exist", path); - return; - } - - if (_environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows) - { - if (path.StartsWith("\\\\", StringComparison.OrdinalIgnoreCase) || path.StartsWith("smb://", StringComparison.OrdinalIgnoreCase)) - { - // not supported - return; - } - } - - // Already being watched - if (_fileSystemWatchers.ContainsKey(path)) - { - return; - } - - // Creating a FileSystemWatcher over the LAN can take hundreds of milliseconds, so wrap it in a Task to do them all in parallel - Task.Run(() => - { - try - { - var newWatcher = new FileSystemWatcher(path, "*") - { - IncludeSubdirectories = true - }; - - if (_environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows) - { - newWatcher.InternalBufferSize = 32767; - } - - newWatcher.NotifyFilter = NotifyFilters.CreationTime | - NotifyFilters.DirectoryName | - NotifyFilters.FileName | - NotifyFilters.LastWrite | - NotifyFilters.Size | - NotifyFilters.Attributes; - - newWatcher.Created += watcher_Changed; - - // Seeing mono crashes on background threads we can't catch, testing if this might help - if (_environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows) - { - newWatcher.Deleted += watcher_Changed; - } - - newWatcher.Renamed += watcher_Changed; - newWatcher.Changed += watcher_Changed; - - newWatcher.Error += watcher_Error; - - if (_fileSystemWatchers.TryAdd(path, newWatcher)) - { - newWatcher.EnableRaisingEvents = true; - Logger.Info("Watching directory " + path); - } - else - { - newWatcher.Dispose(); - } - - } - catch (Exception ex) - { - Logger.ErrorException("Error watching path: {0}", ex, path); - } - }); - } - - /// - /// Stops the watching path. - /// - /// The path. - private void StopWatchingPath(string path) - { - FileSystemWatcher watcher; - - if (_fileSystemWatchers.TryGetValue(path, out watcher)) - { - DisposeWatcher(watcher); - } - } - - /// - /// Disposes the watcher. - /// - /// The watcher. - private void DisposeWatcher(FileSystemWatcher watcher) - { - try - { - using (watcher) - { - Logger.Info("Stopping directory watching for path {0}", watcher.Path); - - watcher.EnableRaisingEvents = false; - } - } - catch - { - - } - finally - { - RemoveWatcherFromList(watcher); - } - } - - /// - /// Removes the watcher from list. - /// - /// The watcher. - private void RemoveWatcherFromList(FileSystemWatcher watcher) - { - FileSystemWatcher removed; - - _fileSystemWatchers.TryRemove(watcher.Path, out removed); - } - - /// - /// Handles the Error event of the watcher control. - /// - /// The source of the event. - /// The instance containing the event data. - void watcher_Error(object sender, ErrorEventArgs e) - { - var ex = e.GetException(); - var dw = (FileSystemWatcher)sender; - - Logger.ErrorException("Error in Directory watcher for: " + dw.Path, ex); - - DisposeWatcher(dw); - } - - /// - /// Handles the Changed event of the watcher control. - /// - /// The source of the event. - /// The instance containing the event data. - void watcher_Changed(object sender, FileSystemEventArgs e) - { - try - { - //Logger.Debug("Changed detected of type " + e.ChangeType + " to " + e.FullPath); - - var path = e.FullPath; - - ReportFileSystemChanged(path); - } - catch (Exception ex) - { - Logger.ErrorException("Exception in ReportFileSystemChanged. Path: {0}", ex, e.FullPath); - } - } - - public void ReportFileSystemChanged(string path) - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException("path"); - } - - var filename = Path.GetFileName(path); - - var monitorPath = !string.IsNullOrEmpty(filename) && - !_alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase) && - !_alwaysIgnoreExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase) && - _alwaysIgnoreSubstrings.All(i => path.IndexOf(i, StringComparison.OrdinalIgnoreCase) == -1); - - // Ignore certain files - var tempIgnorePaths = _tempIgnoredPaths.Keys.ToList(); - - // If the parent of an ignored path has a change event, ignore that too - if (tempIgnorePaths.Any(i => - { - if (_fileSystem.AreEqual(i, path)) - { - Logger.Debug("Ignoring change to {0}", path); - return true; - } - - if (_fileSystem.ContainsSubPath(i, path)) - { - Logger.Debug("Ignoring change to {0}", path); - return true; - } - - // Go up a level - var parent = _fileSystem.GetDirectoryName(i); - if (!string.IsNullOrEmpty(parent)) - { - if (_fileSystem.AreEqual(parent, path)) - { - Logger.Debug("Ignoring change to {0}", path); - return true; - } - } - - return false; - - })) - { - monitorPath = false; - } - - if (monitorPath) - { - // Avoid implicitly captured closure - CreateRefresher(path); - } - } - - private void CreateRefresher(string path) - { - var parentPath = _fileSystem.GetDirectoryName(path); - - lock (_activeRefreshers) - { - var refreshers = _activeRefreshers.ToList(); - foreach (var refresher in refreshers) - { - // Path is already being refreshed - if (_fileSystem.AreEqual(path, refresher.Path)) - { - refresher.RestartTimer(); - return; - } - - // Parent folder is already being refreshed - if (_fileSystem.ContainsSubPath(refresher.Path, path)) - { - refresher.AddPath(path); - return; - } - - // New path is a parent - if (_fileSystem.ContainsSubPath(path, refresher.Path)) - { - refresher.ResetPath(path, null); - return; - } - - // They are siblings. Rebase the refresher to the parent folder. - if (string.Equals(parentPath, _fileSystem.GetDirectoryName(refresher.Path), StringComparison.Ordinal)) - { - refresher.ResetPath(parentPath, path); - return; - } - } - - var newRefresher = new FileRefresher(path, _fileSystem, ConfigurationManager, LibraryManager, TaskManager, Logger, _timerFactory, _environmentInfo, LibraryManager); - newRefresher.Completed += NewRefresher_Completed; - _activeRefreshers.Add(newRefresher); - } - } - - private void NewRefresher_Completed(object sender, EventArgs e) - { - var refresher = (FileRefresher)sender; - DisposeRefresher(refresher); - } - - /// - /// Stops this instance. - /// - public void Stop() - { - LibraryManager.ItemAdded -= LibraryManager_ItemAdded; - LibraryManager.ItemRemoved -= LibraryManager_ItemRemoved; - - foreach (var watcher in _fileSystemWatchers.Values.ToList()) - { - watcher.Created -= watcher_Changed; - watcher.Deleted -= watcher_Changed; - watcher.Renamed -= watcher_Changed; - watcher.Changed -= watcher_Changed; - - try - { - watcher.EnableRaisingEvents = false; - } - catch (InvalidOperationException) - { - // Seeing this under mono on linux sometimes - // Collection was modified; enumeration operation may not execute. - } - - watcher.Dispose(); - } - - _fileSystemWatchers.Clear(); - DisposeRefreshers(); - } - - private void DisposeRefresher(FileRefresher refresher) - { - lock (_activeRefreshers) - { - refresher.Dispose(); - _activeRefreshers.Remove(refresher); - } - } - - private void DisposeRefreshers() - { - lock (_activeRefreshers) - { - foreach (var refresher in _activeRefreshers.ToList()) - { - refresher.Dispose(); - } - _activeRefreshers.Clear(); - } - } - - private bool _disposed; - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - _disposed = true; - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - Stop(); - } - } - } - - public class LibraryMonitorStartup : IServerEntryPoint - { - private readonly ILibraryMonitor _monitor; - - public LibraryMonitorStartup(ILibraryMonitor monitor) - { - _monitor = monitor; - } - - public void Run() - { - _monitor.Start(); - } - - public void Dispose() - { - } - } -} diff --git a/Emby.Server.Core/IO/MemoryStreamProvider.cs b/Emby.Server.Core/IO/MemoryStreamProvider.cs deleted file mode 100644 index f6dd1ecbc2..0000000000 --- a/Emby.Server.Core/IO/MemoryStreamProvider.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.IO; -using MediaBrowser.Model.IO; -using Microsoft.IO; - -namespace Emby.Server.Core.IO -{ - public class RecyclableMemoryStreamProvider : IMemoryStreamFactory - { - readonly RecyclableMemoryStreamManager _manager = new RecyclableMemoryStreamManager(); - - public MemoryStream CreateNew() - { - return _manager.GetStream(); - } - - public MemoryStream CreateNew(int capacity) - { - return _manager.GetStream("RecyclableMemoryStream", capacity); - } - - public MemoryStream CreateNew(byte[] buffer) - { - return _manager.GetStream("RecyclableMemoryStream", buffer, 0, buffer.Length); - } - - public bool TryGetBuffer(MemoryStream stream, out byte[] buffer) - { - buffer = stream.GetBuffer(); - return true; - } - } - - public class MemoryStreamProvider : IMemoryStreamFactory - { - public MemoryStream CreateNew() - { - return new MemoryStream(); - } - - public MemoryStream CreateNew(int capacity) - { - return new MemoryStream(capacity); - } - - public MemoryStream CreateNew(byte[] buffer) - { - return new MemoryStream(buffer); - } - - public bool TryGetBuffer(MemoryStream stream, out byte[] buffer) - { - buffer = stream.GetBuffer(); - return true; - } - } -} diff --git a/Emby.Server.Core/Localization/TextLocalizer.cs b/Emby.Server.Core/Localization/TextLocalizer.cs deleted file mode 100644 index 1e8ccbbfa8..0000000000 --- a/Emby.Server.Core/Localization/TextLocalizer.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using Emby.Server.Implementations.Localization; - -namespace Emby.Server.Core.Localization -{ - public class TextLocalizer : ITextLocalizer - { - public string RemoveDiacritics(string text) - { - if (text == null) - { - throw new ArgumentNullException("text"); - } - - var chars = Normalize(text, NormalizationForm.FormD) - .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) != UnicodeCategory.NonSpacingMark); - - return Normalize(String.Concat(chars), NormalizationForm.FormC); - } - - private static string Normalize(string text, NormalizationForm form, bool stripStringOnFailure = true) - { - if (stripStringOnFailure) - { - try - { - return text.Normalize(form); - } - catch (ArgumentException) - { - // will throw if input contains invalid unicode chars - // https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/ - text = StripInvalidUnicodeCharacters(text); - return Normalize(text, form, false); - } - } - - try - { - return text.Normalize(form); - } - catch (ArgumentException) - { - // if it still fails, return the original text - return text; - } - } - - private static string StripInvalidUnicodeCharacters(string str) - { - var invalidCharactersRegex = new Regex("([\ud800-\udbff](?![\udc00-\udfff]))|((? - - - - - - - - - - diff --git a/Emby.Server.Core/packages.config b/Emby.Server.Core/packages.config deleted file mode 100644 index 6311b55eb2..0000000000 --- a/Emby.Server.Core/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index 567f139fd8..702917832b 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -18,6 +18,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Extensions; namespace Emby.Server.Implementations.Activity { @@ -436,7 +437,7 @@ namespace Emby.Server.Implementations.Activity { Name = string.Format(_localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name), Type = "ScheduledTaskFailed", - Overview = string.Join(Environment.NewLine, vals.ToArray()), + Overview = string.Join(Environment.NewLine, vals.ToArray(vals.Count)), ShortOverview = runningTime, Severity = LogSeverity.Error }); diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index e9b6f7a404..7720f8f2fd 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -10,6 +10,7 @@ using MediaBrowser.Model.Activity; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Querying; using SQLitePCL.pretty; +using MediaBrowser.Model.Extensions; namespace Emby.Server.Implementations.Activity { @@ -94,13 +95,13 @@ namespace Emby.Server.Implementations.Activity var whereTextWithoutPaging = whereClauses.Count == 0 ? string.Empty : - " where " + string.Join(" AND ", whereClauses.ToArray()); + " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count)); if (startIndex.HasValue && startIndex.Value > 0) { var pagingWhereText = whereClauses.Count == 0 ? string.Empty : - " where " + string.Join(" AND ", whereClauses.ToArray()); + " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count)); whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM ActivityLogEntries {0} ORDER BY DateCreated DESC LIMIT {1})", pagingWhereText, @@ -109,7 +110,7 @@ namespace Emby.Server.Implementations.Activity var whereText = whereClauses.Count == 0 ? string.Empty : - " where " + string.Join(" AND ", whereClauses.ToArray()); + " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count)); commandText += whereText; @@ -154,7 +155,7 @@ namespace Emby.Server.Implementations.Activity result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First(); } - result.Items = list.ToArray(); + result.Items = list.ToArray(list.Count); return result; }, ReadTransactionMode); diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs new file mode 100644 index 0000000000..f1f24660c5 --- /dev/null +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -0,0 +1,1749 @@ +using Emby.Common.Implementations; +using Emby.Common.Implementations.Archiving; +using Emby.Common.Implementations.IO; +using Emby.Common.Implementations.Reflection; +using Emby.Common.Implementations.ScheduledTasks; +using Emby.Common.Implementations.Serialization; +using Emby.Common.Implementations.TextEncoding; +using Emby.Common.Implementations.Xml; +using Emby.Dlna; +using Emby.Dlna.ConnectionManager; +using Emby.Dlna.ContentDirectory; +using Emby.Dlna.Main; +using Emby.Dlna.MediaReceiverRegistrar; +using Emby.Dlna.Ssdp; +using Emby.Drawing; +using Emby.Photos; +using Emby.Server.Implementations.Activity; +using Emby.Server.Implementations.Channels; +using Emby.Server.Implementations.Collections; +using Emby.Server.Implementations.Configuration; +using Emby.Server.Implementations.Data; +using Emby.Server.Implementations.Devices; +using Emby.Server.Implementations.Dto; +using Emby.Server.Implementations.FFMpeg; +using Emby.Server.Implementations.HttpServer; +using Emby.Server.Implementations.HttpServer.Security; +using Emby.Server.Implementations.IO; +using Emby.Server.Implementations.Library; +using Emby.Server.Implementations.LiveTv; +using Emby.Server.Implementations.Localization; +using Emby.Server.Implementations.MediaEncoder; +using Emby.Server.Implementations.Migrations; +using Emby.Server.Implementations.Notifications; +using Emby.Server.Implementations.Playlists; +using Emby.Server.Implementations.Security; +using Emby.Server.Implementations.Session; +using Emby.Server.Implementations.Social; +using Emby.Server.Implementations.TV; +using Emby.Server.Implementations.Updates; +using MediaBrowser.Api; +using MediaBrowser.Common; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Events; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Net; +using MediaBrowser.Common.Plugins; +using MediaBrowser.Common.Progress; +using MediaBrowser.Common.Security; +using MediaBrowser.Common.Updates; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Chapters; +using MediaBrowser.Controller.Collections; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Connect; +using MediaBrowser.Controller.Devices; +using MediaBrowser.Controller.Dlna; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Playlists; +using MediaBrowser.Controller.Plugins; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Controller.Resolvers; +using MediaBrowser.Controller.Security; +using MediaBrowser.Controller.Session; +using MediaBrowser.Controller.Sorting; +using MediaBrowser.Controller.Subtitles; +using MediaBrowser.Controller.Sync; +using MediaBrowser.Controller.TV; +using MediaBrowser.LocalMetadata.Savers; +using MediaBrowser.Model.Activity; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Diagnostics; +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Net; +using MediaBrowser.Model.News; +using MediaBrowser.Model.Reflection; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Services; +using MediaBrowser.Model.Social; +using MediaBrowser.Model.System; +using MediaBrowser.Model.Text; +using MediaBrowser.Model.Updates; +using MediaBrowser.Model.Xml; +using MediaBrowser.Providers.Chapters; +using MediaBrowser.Providers.Manager; +using MediaBrowser.Providers.Subtitles; +using MediaBrowser.WebDashboard.Api; +using MediaBrowser.XbmcMetadata.Providers; +using OpenSubtitlesHandler; +using ServiceStack; +using SocketHttpListener.Primitives; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; +using Emby.Server.MediaEncoding.Subtitles; +using MediaBrowser.MediaEncoding.BdInfo; +using StringExtensions = MediaBrowser.Controller.Extensions.StringExtensions; + +namespace Emby.Server.Implementations +{ + /// + /// Class CompositionRoot + /// + public abstract class ApplicationHost : BaseApplicationHost, IServerApplicationHost, IDependencyContainer + { + /// + /// Gets the server configuration manager. + /// + /// The server configuration manager. + public IServerConfigurationManager ServerConfigurationManager + { + get { return (IServerConfigurationManager)ConfigurationManager; } + } + + /// + /// Gets the configuration manager. + /// + /// IConfigurationManager. + protected override IConfigurationManager GetConfigurationManager() + { + return new ServerConfigurationManager(ApplicationPaths, LogManager, XmlSerializer, FileSystemManager); + } + + /// + /// Gets or sets the server manager. + /// + /// The server manager. + private IServerManager ServerManager { get; set; } + /// + /// Gets or sets the user manager. + /// + /// The user manager. + public IUserManager UserManager { get; set; } + /// + /// Gets or sets the library manager. + /// + /// The library manager. + internal ILibraryManager LibraryManager { get; set; } + /// + /// Gets or sets the directory watchers. + /// + /// The directory watchers. + private ILibraryMonitor LibraryMonitor { get; set; } + /// + /// Gets or sets the provider manager. + /// + /// The provider manager. + private IProviderManager ProviderManager { get; set; } + /// + /// Gets or sets the HTTP server. + /// + /// The HTTP server. + private IHttpServer HttpServer { get; set; } + private IDtoService DtoService { get; set; } + public IImageProcessor ImageProcessor { get; set; } + + /// + /// Gets or sets the media encoder. + /// + /// The media encoder. + private IMediaEncoder MediaEncoder { get; set; } + private ISubtitleEncoder SubtitleEncoder { get; set; } + + private IConnectManager ConnectManager { get; set; } + private ISessionManager SessionManager { get; set; } + + private ILiveTvManager LiveTvManager { get; set; } + + public ILocalizationManager LocalizationManager { get; set; } + + private IEncodingManager EncodingManager { get; set; } + private IChannelManager ChannelManager { get; set; } + private ISyncManager SyncManager { get; set; } + + /// + /// Gets or sets the user data repository. + /// + /// The user data repository. + private IUserDataManager UserDataManager { get; set; } + private IUserRepository UserRepository { get; set; } + internal IDisplayPreferencesRepository DisplayPreferencesRepository { get; set; } + internal IItemRepository ItemRepository { get; set; } + private INotificationsRepository NotificationsRepository { get; set; } + + private INotificationManager NotificationManager { get; set; } + private ISubtitleManager SubtitleManager { get; set; } + private IChapterManager ChapterManager { get; set; } + private IDeviceManager DeviceManager { get; set; } + + internal IUserViewManager UserViewManager { get; set; } + + private IAuthenticationRepository AuthenticationRepository { get; set; } + private ISyncRepository SyncRepository { get; set; } + private ITVSeriesManager TVSeriesManager { get; set; } + private ICollectionManager CollectionManager { get; set; } + private IMediaSourceManager MediaSourceManager { get; set; } + private IPlaylistManager PlaylistManager { get; set; } + + /// + /// Gets or sets the installation manager. + /// + /// The installation manager. + protected IInstallationManager InstallationManager { get; private set; } + /// + /// Gets the security manager. + /// + /// The security manager. + protected ISecurityManager SecurityManager { get; private set; } + + /// + /// Gets or sets the zip client. + /// + /// The zip client. + protected IZipClient ZipClient { get; private set; } + + protected IAuthService AuthService { get; private set; } + + protected readonly StartupOptions StartupOptions; + private readonly string _releaseAssetFilename; + + internal IPowerManagement PowerManagement { get; private set; } + internal IImageEncoder ImageEncoder { get; private set; } + + private readonly Action _certificateGenerator; + private readonly Func _defaultUserNameFactory; + + /// + /// Initializes a new instance of the class. + /// + public ApplicationHost(ServerApplicationPaths applicationPaths, + ILogManager logManager, + StartupOptions options, + IFileSystem fileSystem, + IPowerManagement powerManagement, + string releaseAssetFilename, + IEnvironmentInfo environmentInfo, + IImageEncoder imageEncoder, + ISystemEvents systemEvents, + IMemoryStreamFactory memoryStreamFactory, + INetworkManager networkManager, + Action certificateGenerator, + Func defaultUsernameFactory) + : base(applicationPaths, + logManager, + fileSystem, + environmentInfo, + systemEvents, + memoryStreamFactory, + networkManager) + { + StartupOptions = options; + _certificateGenerator = certificateGenerator; + _releaseAssetFilename = releaseAssetFilename; + _defaultUserNameFactory = defaultUsernameFactory; + PowerManagement = powerManagement; + + ImageEncoder = imageEncoder; + + SetBaseExceptionMessage(); + + if (environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows) + { + fileSystem.AddShortcutHandler(new LnkShortcutHandler()); + } + + fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); + } + + private Version _version; + /// + /// Gets the current application version + /// + /// The application version. + public override Version ApplicationVersion + { + get + { + return _version ?? (_version = GetAssembly(GetType()).GetName().Version); + } + } + + public virtual bool SupportsRunningAsService + { + get + { + return false; + } + } + + /// + /// Gets the name. + /// + /// The name. + public override string Name + { + get + { + return "Emby Server"; + } + } + + public virtual bool IsRunningAsService + { + get + { + return false; + } + } + + private Assembly GetAssembly(Type type) + { + return type.GetTypeInfo().Assembly; + } + + public virtual bool SupportsAutoRunAtStartup + { + get + { + return EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows; + } + } + + private void SetBaseExceptionMessage() + { + var builder = GetBaseExceptionMessage(ApplicationPaths); + + builder.Insert(0, string.Format("Version: {0}{1}", ApplicationVersion, Environment.NewLine)); + builder.Insert(0, "*** Error Report ***" + Environment.NewLine); + + LogManager.ExceptionMessagePrefix = builder.ToString(); + } + + /// + /// Runs the startup tasks. + /// + public override async Task RunStartupTasks() + { + await PerformPreInitMigrations().ConfigureAwait(false); + + await base.RunStartupTasks().ConfigureAwait(false); + + await MediaEncoder.Init().ConfigureAwait(false); + + if (string.IsNullOrWhiteSpace(MediaEncoder.EncoderPath)) + { + if (ServerConfigurationManager.Configuration.IsStartupWizardCompleted) + { + ServerConfigurationManager.Configuration.IsStartupWizardCompleted = false; + ServerConfigurationManager.SaveConfiguration(); + } + } + + Logger.Info("ServerId: {0}", SystemId); + Logger.Info("Core startup complete"); + HttpServer.GlobalResponse = null; + + PerformPostInitMigrations(); + Logger.Info("Post-init migrations complete"); + + foreach (var entryPoint in GetExports().ToList()) + { + var name = entryPoint.GetType().FullName; + Logger.Info("Starting entry point {0}", name); + var now = DateTime.UtcNow; + try + { + entryPoint.Run(); + } + catch (Exception ex) + { + Logger.ErrorException("Error in {0}", ex, name); + } + Logger.Info("Entry point completed: {0}. Duration: {1} seconds", name, (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture), "ImageInfos"); + } + Logger.Info("All entry points have started"); + + LogManager.RemoveConsoleOutput(); + } + + protected override IJsonSerializer CreateJsonSerializer() + { + try + { + // https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.WebHost.IntegrationTests/Web.config#L4 + Licensing.RegisterLicense("1001-e1JlZjoxMDAxLE5hbWU6VGVzdCBCdXNpbmVzcyxUeXBlOkJ1c2luZXNzLEhhc2g6UHVNTVRPclhvT2ZIbjQ5MG5LZE1mUTd5RUMzQnBucTFEbTE3TDczVEF4QUNMT1FhNXJMOWkzVjFGL2ZkVTE3Q2pDNENqTkQyUktRWmhvUVBhYTBiekJGUUZ3ZE5aZHFDYm9hL3lydGlwUHI5K1JsaTBYbzNsUC85cjVJNHE5QVhldDN6QkE4aTlvdldrdTgyTk1relY2eis2dFFqTThYN2lmc0JveHgycFdjPSxFeHBpcnk6MjAxMy0wMS0wMX0="); + } + catch + { + // Failing under mono + } + + var result = new JsonSerializer(FileSystemManager, LogManager.GetLogger("JsonSerializer")); + + ServiceStack.Text.JsConfig.ExcludePropertyNames = new[] { "ProviderIds" }; + ServiceStack.Text.JsConfig.ExcludePropertyNames = new[] { "ProviderIds" }; + ServiceStack.Text.JsConfig.ExcludePropertyNames = new[] { "ProviderIds" }; + ServiceStack.Text.JsConfig.ExcludePropertyNames = new[] { "ProviderIds" }; + ServiceStack.Text.JsConfig.ExcludePropertyNames = new[] { "ProviderIds" }; + ServiceStack.Text.JsConfig