diff options
29 files changed, 300 insertions, 201 deletions
diff --git a/Emby.Common.Implementations/EnvironmentInfo/EnvironmentInfo.cs b/Emby.Common.Implementations/EnvironmentInfo/EnvironmentInfo.cs index 6cc4626ea..dcc974413 100644 --- a/Emby.Common.Implementations/EnvironmentInfo/EnvironmentInfo.cs +++ b/Emby.Common.Implementations/EnvironmentInfo/EnvironmentInfo.cs @@ -10,11 +10,17 @@ namespace Emby.Common.Implementations.EnvironmentInfo public class EnvironmentInfo : IEnvironmentInfo { public MediaBrowser.Model.System.Architecture? CustomArchitecture { get; set; } + public MediaBrowser.Model.System.OperatingSystem? CustomOperatingSystem { get; set; } public MediaBrowser.Model.System.OperatingSystem OperatingSystem { get { + if (CustomOperatingSystem.HasValue) + { + return CustomOperatingSystem.Value; + } + #if NET46 switch (Environment.OSVersion.Platform) { diff --git a/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index cbc7c7c2d..de528a94f 100644 --- a/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -282,9 +282,12 @@ namespace Emby.Common.Implementations.ScheduledTasks throw new ArgumentNullException("value"); } - SaveTriggers(value); + // This null check is not great, but is needed to handle bad user input, or user mucking with the config file incorrectly + var triggerList = value.Where(i => i != null).ToArray(); - InternalTriggers = value.Select(i => new Tuple<TaskTriggerInfo, ITaskTrigger>(i, GetTrigger(i))).ToArray(); + SaveTriggers(triggerList); + + InternalTriggers = triggerList.Select(i => new Tuple<TaskTriggerInfo, ITaskTrigger>(i, GetTrigger(i))).ToArray(); } } @@ -535,7 +538,8 @@ namespace Emby.Common.Implementations.ScheduledTasks /// <returns>IEnumerable{BaseTaskTrigger}.</returns> private Tuple<TaskTriggerInfo, ITaskTrigger>[] LoadTriggers() { - var settings = LoadTriggerSettings(); + // This null check is not great, but is needed to handle bad user input, or user mucking with the config file incorrectly + var settings = LoadTriggerSettings().Where(i => i != null).ToArray(); return settings.Select(i => new Tuple<TaskTriggerInfo, ITaskTrigger>(i, GetTrigger(i))).ToArray(); } @@ -544,8 +548,12 @@ namespace Emby.Common.Implementations.ScheduledTasks { try { - return JsonSerializer.DeserializeFromFile<IEnumerable<TaskTriggerInfo>>(GetConfigurationFilePath()) - .ToArray(); + var list = JsonSerializer.DeserializeFromFile<IEnumerable<TaskTriggerInfo>>(GetConfigurationFilePath()); + + if (list != null) + { + return list.ToArray(); + } } catch (FileNotFoundException) { @@ -555,8 +563,8 @@ namespace Emby.Common.Implementations.ScheduledTasks catch (DirectoryNotFoundException) { // File doesn't exist. No biggie. Return defaults. - return ScheduledTask.GetDefaultTriggers().ToArray(); } + return ScheduledTask.GetDefaultTriggers().ToArray(); } /// <summary> diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index bba8c53d9..432991128 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -23,6 +23,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; +using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Xml; @@ -464,6 +465,16 @@ namespace Emby.Dlna.ContentDirectory private async Task<QueryResult<ServerItem>> GetUserItems(BaseItem item, StubType? stubType, User user, SortCriteria sort, int? startIndex, int? limit) { + if (item is MusicGenre) + { + return GetMusicGenreItems(item, null, user, sort, startIndex, limit); + } + + if (item is MusicArtist) + { + return GetMusicArtistItems(item, null, user, sort, startIndex, limit); + } + if (stubType.HasValue) { if (stubType.Value == StubType.People) @@ -476,7 +487,7 @@ namespace Emby.Dlna.ContentDirectory var result = new QueryResult<ServerItem> { - Items = items.Select(i => new ServerItem { Item = i, StubType = StubType.Folder }).ToArray(), + Items = items.Select(i => new ServerItem(i)).ToArray(), TotalRecordCount = items.Length }; @@ -494,41 +505,88 @@ namespace Emby.Dlna.ContentDirectory var folder = (Folder)item; - var sortOrders = new List<string>(); - if (!folder.IsPreSorted) - { - sortOrders.Add(ItemSortBy.SortName); - } - - var queryResult = await folder.GetItems(new InternalItemsQuery + var query = new InternalItemsQuery { Limit = limit, StartIndex = startIndex, - SortBy = sortOrders.ToArray(), - SortOrder = sort.SortOrder, User = user, IsMissing = false, - PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Music }, - ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name }, + PresetViews = new[] {CollectionType.Movies, CollectionType.TvShows, CollectionType.Music}, + ExcludeItemTypes = new[] {typeof (Game).Name, typeof (Book).Name}, IsPlaceHolder = false + }; + + SetSorting(query, sort, folder.IsPreSorted); + + var queryResult = await folder.GetItems(query).ConfigureAwait(false); + + return ToResult(queryResult); + } + + private QueryResult<ServerItem> GetMusicArtistItems(BaseItem item, Guid? parentId, User user, SortCriteria sort, int? startIndex, int? limit) + { + var query = new InternalItemsQuery(user) + { + Recursive = true, + ParentId = parentId, + ArtistIds = new[] { item.Id.ToString("N") }, + IncludeItemTypes = new[] { typeof(MusicAlbum).Name }, + Limit = limit, + StartIndex = startIndex + }; + + SetSorting(query, sort, false); + + var result = _libraryManager.GetItemsResult(query); - }).ConfigureAwait(false); + return ToResult(result); + } + + private QueryResult<ServerItem> GetMusicGenreItems(BaseItem item, Guid? parentId, User user, SortCriteria sort, int? startIndex, int? limit) + { + var query = new InternalItemsQuery(user) + { + Recursive = true, + ParentId = parentId, + GenreIds = new[] {item.Id.ToString("N")}, + IncludeItemTypes = new[] {typeof (MusicAlbum).Name}, + Limit = limit, + StartIndex = startIndex + }; - var serverItems = queryResult + SetSorting(query, sort, false); + + var result = _libraryManager.GetItemsResult(query); + + return ToResult(result); + } + + private QueryResult<ServerItem> ToResult(QueryResult<BaseItem> result) + { + var serverItems = result .Items - .Select(i => new ServerItem - { - Item = i - }) + .Select(i => new ServerItem(i)) .ToArray(); return new QueryResult<ServerItem> { - TotalRecordCount = queryResult.TotalRecordCount, + TotalRecordCount = result.TotalRecordCount, Items = serverItems }; } + private void SetSorting(InternalItemsQuery query, SortCriteria sort, bool isPreSorted) + { + var sortOrders = new List<string>(); + if (!isPreSorted) + { + sortOrders.Add(ItemSortBy.SortName); + } + + query.SortBy = sortOrders.ToArray(); + query.SortOrder = sort.SortOrder; + } + private QueryResult<ServerItem> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit) { var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user) @@ -541,11 +599,7 @@ namespace Emby.Dlna.ContentDirectory }); - var serverItems = itemsResult.Items.Select(i => new ServerItem - { - Item = i, - StubType = null - }) + var serverItems = itemsResult.Items.Select(i => new ServerItem(i)) .ToArray(); return new QueryResult<ServerItem> @@ -566,7 +620,7 @@ namespace Emby.Dlna.ContentDirectory { return DidlBuilder.IsIdRoot(id) - ? new ServerItem { Item = user.RootFolder } + ? new ServerItem(user.RootFolder) : ParseItemId(id, user); } @@ -601,16 +655,15 @@ namespace Emby.Dlna.ContentDirectory { var item = _libraryManager.GetItemById(itemId); - return new ServerItem + return new ServerItem(item) { - Item = item, StubType = stubType }; } Logger.Error("Error parsing item Id: {0}. Returning user root folder.", id); - return new ServerItem { Item = user.RootFolder }; + return new ServerItem(user.RootFolder); } } @@ -618,6 +671,16 @@ namespace Emby.Dlna.ContentDirectory { public BaseItem Item { get; set; } public StubType? StubType { get; set; } + + public ServerItem(BaseItem item) + { + Item = item; + + if (item is IItemByName && !(item is Folder)) + { + StubType = Dlna.ContentDirectory.StubType.Folder; + } + } } public enum StubType diff --git a/Emby.Server.Core/ApplicationHost.cs b/Emby.Server.Core/ApplicationHost.cs index a0a7416e7..3590ade40 100644 --- a/Emby.Server.Core/ApplicationHost.cs +++ b/Emby.Server.Core/ApplicationHost.cs @@ -1583,7 +1583,8 @@ namespace Emby.Server.Core public void LaunchUrl(string url) { - if (EnvironmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows) + if (EnvironmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows && + EnvironmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.OSX) { throw new NotImplementedException(); } @@ -1591,7 +1592,7 @@ namespace Emby.Server.Core var process = ProcessFactory.Create(new ProcessOptions { FileName = url, - EnableRaisingEvents = true, + //EnableRaisingEvents = true, UseShellExecute = true, ErrorDialog = false }); diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 151702905..8e6a277a4 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -3855,16 +3855,18 @@ namespace Emby.Server.Implementations.Data } } - if (query.ArtistNames.Length > 0) + if (query.ArtistIds.Length > 0) { var clauses = new List<string>(); var index = 0; - foreach (var artist in query.ArtistNames) + foreach (var artistId in query.ArtistIds) { - clauses.Add("@ArtistName" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type <= 1)"); + var paramName = "@ArtistIds" + index; + + clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type<=1)"); if (statement != null) { - statement.TryBind("@ArtistName" + index, GetCleanValue(artist)); + statement.TryBind(paramName, artistId.ToGuidParamValue()); } index++; } @@ -3963,23 +3965,6 @@ namespace Emby.Server.Implementations.Data whereClauses.Add(clause); } - if (query.Studios.Length > 0) - { - var clauses = new List<string>(); - var index = 0; - foreach (var item in query.Studios) - { - clauses.Add("@Studio" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=3)"); - if (statement != null) - { - statement.TryBind("@Studio" + index, GetCleanValue(item)); - } - index++; - } - var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")"; - whereClauses.Add(clause); - } - if (query.Keywords.Length > 0) { var clauses = new List<string>(); diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 995dc7b7b..e78446bc8 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -203,20 +203,12 @@ namespace Emby.Server.Implementations.HttpServer // Do not use the memoryStreamFactory here, they don't place nice with compression using (var ms = new MemoryStream()) { - using (var compressionStream = GetCompressionStream(ms, compressionType)) - { - ContentTypes.Instance.SerializeToStream(request, dto, compressionStream); - compressionStream.Dispose(); - - var compressedBytes = ms.ToArray(); + ContentTypes.Instance.SerializeToStream(request, dto, ms); + ms.Position = 0; - var httpResult = new StreamWriter(compressedBytes, request.ResponseContentType, _logger); + var responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); - //httpResult.Headers["Content-Length"] = compressedBytes.Length.ToString(UsCulture); - httpResult.Headers["Content-Encoding"] = compressionType; - - return httpResult; - } + return GetCompressedResult(ms, compressionType, responseHeaders, false, request.ResponseContentType).Result; } } @@ -591,45 +583,53 @@ namespace Emby.Server.Implementations.HttpServer }; } - string content; - using (var stream = await factoryFn().ConfigureAwait(false)) { - using (var reader = new StreamReader(stream)) + return await GetCompressedResult(stream, requestedCompressionType, responseHeaders, isHeadRequest, contentType).ConfigureAwait(false); + } + } + + private async Task<IHasHeaders> GetCompressedResult(Stream stream, + string requestedCompressionType, + IDictionary<string,string> responseHeaders, + bool isHeadRequest, + string contentType) + { + using (var reader = new MemoryStream()) + { + await stream.CopyToAsync(reader).ConfigureAwait(false); + + reader.Position = 0; + var content = reader.ToArray(); + + if (content.Length >= 1024) { - content = await reader.ReadToEndAsync().ConfigureAwait(false); + content = Compress(content, requestedCompressionType); + responseHeaders["Content-Encoding"] = requestedCompressionType; } - } - var contents = Compress(content, requestedCompressionType); + responseHeaders["Content-Length"] = content.Length.ToString(UsCulture); - responseHeaders["Content-Length"] = contents.Length.ToString(UsCulture); - responseHeaders["Content-Encoding"] = requestedCompressionType; + if (isHeadRequest) + { + return GetHttpResult(new byte[] { }, contentType, true); + } - if (isHeadRequest) - { - return GetHttpResult(new byte[] { }, contentType, true); + return GetHttpResult(content, contentType, true, responseHeaders); } - - return GetHttpResult(contents, contentType, true, responseHeaders); } - private byte[] Compress(string text, string compressionType) + private byte[] Compress(byte[] bytes, string compressionType) { if (compressionType == "deflate") - return Deflate(text); + return Deflate(bytes); if (compressionType == "gzip") - return GZip(text); + return GZip(bytes); throw new NotSupportedException(compressionType); } - private byte[] Deflate(string text) - { - return Deflate(Encoding.UTF8.GetBytes(text)); - } - private byte[] Deflate(byte[] bytes) { // In .NET FX incompat-ville, you can't access compressed bytes without closing DeflateStream @@ -644,11 +644,6 @@ namespace Emby.Server.Implementations.HttpServer } } - private byte[] GZip(string text) - { - return GZip(Encoding.UTF8.GetBytes(text)); - } - private byte[] GZip(byte[] buffer) { using (var ms = new MemoryStream()) diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 04fc78c95..e2446b16f 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings } var showType = details.showType ?? string.Empty; - + var info = new ProgramInfo { ChannelId = channel, @@ -440,10 +440,23 @@ namespace Emby.Server.Implementations.LiveTv.Listings IsKids = string.Equals(details.audience, "children", StringComparison.OrdinalIgnoreCase), IsSports = showType.IndexOf("sports", StringComparison.OrdinalIgnoreCase) != -1, IsMovie = showType.IndexOf("movie", StringComparison.OrdinalIgnoreCase) != -1 || showType.IndexOf("film", StringComparison.OrdinalIgnoreCase) != -1, - ShowId = programInfo.programID, Etag = programInfo.md5 }; + var showId = programInfo.programID ?? string.Empty; + + // According to SchedulesDirect, these are generic, unidentified episodes + // SH005316560000 + var hasUniqueShowId = !showId.StartsWith("SH", StringComparison.OrdinalIgnoreCase) || + !showId.EndsWith("0000", StringComparison.OrdinalIgnoreCase); + + if (!hasUniqueShowId) + { + showId = newID; + } + + info.ShowId = showId; + if (programInfo.videoProperties != null) { info.IsHD = programInfo.videoProperties.Contains("hdtv", StringComparer.OrdinalIgnoreCase); diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 525db4036..62a0738c7 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -2647,7 +2647,7 @@ namespace Emby.Server.Implementations.LiveTv public GuideInfo GetGuideInfo() { var startDate = DateTime.UtcNow; - var endDate = startDate.AddDays(14); + var endDate = startDate.AddDays(GetGuideDays()); return new GuideInfo { diff --git a/Emby.Server.Implementations/Migrations/LibraryScanMigration.cs b/Emby.Server.Implementations/Migrations/LibraryScanMigration.cs index c494abc0b..600dd8033 100644 --- a/Emby.Server.Implementations/Migrations/LibraryScanMigration.cs +++ b/Emby.Server.Implementations/Migrations/LibraryScanMigration.cs @@ -33,10 +33,10 @@ namespace Emby.Server.Implementations.Migrations { Task.Run(() => { - var task = _taskManager.ScheduledTasks.Select(i => i.ScheduledTask) - .First(i => string.Equals(i.Key, "RefreshLibrary", StringComparison.OrdinalIgnoreCase)); - - _taskManager.QueueScheduledTask(task); + _taskManager.QueueScheduledTask(_taskManager.ScheduledTasks.Select(i => i.ScheduledTask) + .First(i => string.Equals(i.Key, "RefreshLibrary", StringComparison.OrdinalIgnoreCase))); + _taskManager.QueueScheduledTask(_taskManager.ScheduledTasks.Select(i => i.ScheduledTask) + .First(i => string.Equals(i.Key, "RefreshGuide", StringComparison.OrdinalIgnoreCase))); }); var list = _config.Configuration.Migrations.ToList(); diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index e29aa7d5a..7ae64b834 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -153,7 +153,7 @@ namespace MediaBrowser.Api.Playback.Progressive if (!state.RunTimeTicks.HasValue) { - args += " -fflags +genpts -flags +global_header"; + args += " -flags -global_header -fflags +genpts"; } return args; diff --git a/MediaBrowser.Api/Reports/ReportsService.cs b/MediaBrowser.Api/Reports/ReportsService.cs index 5c4d21d66..efbfa1bdf 100644 --- a/MediaBrowser.Api/Reports/ReportsService.cs +++ b/MediaBrowser.Api/Reports/ReportsService.cs @@ -209,7 +209,6 @@ namespace MediaBrowser.Api.Reports OfficialRatings = request.GetOfficialRatings(), Genres = request.GetGenres(), GenreIds = request.GetGenreIds(), - Studios = request.GetStudios(), StudioIds = request.GetStudioIds(), Person = request.Person, PersonIds = request.GetPersonIds(), @@ -314,21 +313,6 @@ namespace MediaBrowser.Api.Reports query.MaxParentalRating = _localization.GetRatingLevel(request.MaxOfficialRating); } - // Artists - if (!string.IsNullOrEmpty(request.ArtistIds)) - { - var artistIds = request.ArtistIds.Split(new[] { '|', ',' }); - - var artistItems = artistIds.Select(_libraryManager.GetItemById).Where(i => i != null).ToList(); - query.ArtistNames = artistItems.Select(i => i.Name).ToArray(); - } - - // Artists - if (!string.IsNullOrEmpty(request.Artists)) - { - query.ArtistNames = request.Artists.Split('|'); - } - // Albums if (!string.IsNullOrEmpty(request.Albums)) { diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index ff285b605..5b939244e 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -124,7 +124,6 @@ namespace MediaBrowser.Api.UserLibrary OfficialRatings = request.GetOfficialRatings(), Genres = request.GetGenres(), GenreIds = request.GetGenreIds(), - Studios = request.GetStudios(), StudioIds = request.GetStudioIds(), Person = request.Person, PersonIds = request.GetPersonIds(), @@ -145,6 +144,22 @@ namespace MediaBrowser.Api.UserLibrary } } + // Studios + if (!string.IsNullOrEmpty(request.Studios)) + { + query.StudioIds = request.Studios.Split('|').Select(i => + { + try + { + return LibraryManager.GetStudio(i); + } + catch + { + return null; + } + }).Where(i => i != null).Select(i => i.Id.ToString("N")).ToArray(); + } + foreach (var filter in request.GetFilters()) { switch (filter) diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs index f106adde5..0594691a2 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs @@ -394,6 +394,11 @@ namespace MediaBrowser.Api.UserLibrary return (Studios ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); } + public string[] GetArtistIds() + { + return (ArtistIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + } + public string[] GetStudioIds() { return (StudioIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index a07128f74..049a7b1c6 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -230,8 +230,8 @@ namespace MediaBrowser.Api.UserLibrary Tags = request.GetTags(), OfficialRatings = request.GetOfficialRatings(), Genres = request.GetGenres(), + ArtistIds = request.GetArtistIds(), GenreIds = request.GetGenreIds(), - Studios = request.GetStudios(), StudioIds = request.GetStudioIds(), Person = request.Person, PersonIds = request.GetPersonIds(), @@ -339,18 +339,19 @@ namespace MediaBrowser.Api.UserLibrary } // Artists - if (!string.IsNullOrEmpty(request.ArtistIds)) - { - var artistIds = request.ArtistIds.Split(new[] { '|', ',' }); - - var artistItems = artistIds.Select(_libraryManager.GetItemById).Where(i => i != null).ToList(); - query.ArtistNames = artistItems.Select(i => i.Name).ToArray(); - } - - // Artists if (!string.IsNullOrEmpty(request.Artists)) { - query.ArtistNames = request.Artists.Split('|'); + query.ArtistIds = request.Artists.Split('|').Select(i => + { + try + { + return _libraryManager.GetArtist(i); + } + catch + { + return null; + } + }).Where(i => i != null).Select(i => i.Id.ToString("N")).ToArray(); } // ExcludeArtistIds @@ -365,6 +366,22 @@ namespace MediaBrowser.Api.UserLibrary query.AlbumNames = request.Albums.Split('|'); } + // Studios + if (!string.IsNullOrEmpty(request.Studios)) + { + query.StudioIds = request.Studios.Split('|').Select(i => + { + try + { + return _libraryManager.GetStudio(i); + } + catch + { + return null; + } + }).Where(i => i != null).Select(i => i.Id.ToString("N")).ToArray(); + } + return query; } } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index fd20f2ab4..1d2716b64 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -69,7 +69,7 @@ namespace MediaBrowser.Controller.Entities.Audio if (query.IncludeItemTypes.Length == 0) { query.IncludeItemTypes = new[] { typeof(Audio).Name, typeof(MusicVideo).Name, typeof(MusicAlbum).Name }; - query.ArtistNames = new[] { Name }; + query.ArtistIds = new[] { Id.ToString("N") }; } return LibraryManager.GetItemList(query); diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index a2d278a71..15af0888d 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -83,7 +83,6 @@ namespace MediaBrowser.Controller.Entities public bool? HasTrailer { get; set; } public bool? HasParentalRating { get; set; } - public string[] Studios { get; set; } public string[] StudioIds { get; set; } public string[] GenreIds { get; set; } public ImageType[] ImageTypes { get; set; } @@ -144,7 +143,7 @@ namespace MediaBrowser.Controller.Entities public string ExternalId { get; set; } public string[] AlbumNames { get; set; } - public string[] ArtistNames { get; set; } + public string[] ArtistIds { get; set; } public string[] ExcludeArtistIds { get; set; } public string AncestorWithPresentationUniqueKey { get; set; } public string SeriesPresentationUniqueKey { get; set; } @@ -203,7 +202,7 @@ namespace MediaBrowser.Controller.Entities DtoOptions = new DtoOptions(); AlbumNames = new string[] { }; - ArtistNames = new string[] { }; + ArtistIds = new string[] { }; ExcludeArtistIds = new string[] { }; ExcludeProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); @@ -216,7 +215,6 @@ namespace MediaBrowser.Controller.Entities IncludeItemTypes = new string[] { }; ExcludeItemTypes = new string[] { }; Genres = new string[] { }; - Studios = new string[] { }; StudioIds = new string[] { }; GenreIds = new string[] { }; ImageTypes = new ImageType[] { }; diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index ec623eeda..c14dd70bf 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -83,7 +83,7 @@ namespace MediaBrowser.Controller.Entities public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query) { - query.Studios = new[] { Name }; + query.StudioIds = new[] { Id.ToString("N") }; return LibraryManager.GetItemList(query); } diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 872011ce8..eea11b167 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -249,18 +249,24 @@ namespace MediaBrowser.Controller.Entities.TV public IEnumerable<Season> GetSeasons(User user) { + var query = new InternalItemsQuery(user); + + SetSeasonQueryOptions(query, user); + + return LibraryManager.GetItemList(query).Cast<Season>(); + } + + private void SetSeasonQueryOptions(InternalItemsQuery query, User user) + { var config = user.Configuration; var enableSeriesPresentationKey = ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey; var seriesKey = GetUniqueSeriesKey(this); - var query = new InternalItemsQuery(user) - { - AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey, - SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null, - IncludeItemTypes = new[] { typeof(Season).Name }, - SortBy = new[] { ItemSortBy.SortName } - }; + query.AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey; + query.SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null; + query.IncludeItemTypes = new[] { typeof(Season).Name }; + query.SortBy = new[] {ItemSortBy.SortName}; if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes) { @@ -274,8 +280,6 @@ namespace MediaBrowser.Controller.Entities.TV { query.IsVirtualUnaired = false; } - - return LibraryManager.GetItemList(query).Cast<Season>(); } protected override Task<QueryResult<BaseItem>> GetItemsInternal(InternalItemsQuery query) @@ -306,11 +310,9 @@ namespace MediaBrowser.Controller.Entities.TV return Task.FromResult(LibraryManager.GetItemsResult(query)); } - Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager); + SetSeasonQueryOptions(query, user); - var items = GetSeasons(user).Where(filter); - var result = PostFilterAndSort(items, query, false, true); - return Task.FromResult(result); + return Task.FromResult(LibraryManager.GetItemsResult(query)); } public IEnumerable<Episode> GetEpisodes(User user) diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index d5781d21e..58d2cdc6c 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -203,9 +203,6 @@ namespace MediaBrowser.Controller.Entities case SpecialFolder.MusicGenres: return GetMusicGenres(queryParent, user, query); - case SpecialFolder.MusicGenre: - return await GetMusicGenreItems(queryParent, displayParent, user, query).ConfigureAwait(false); - case SpecialFolder.MusicLatest: return GetMusicLatest(queryParent, user, query); @@ -306,18 +303,6 @@ namespace MediaBrowser.Controller.Entities }; } - private async Task<QueryResult<BaseItem>> GetMusicGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query) - { - query.Recursive = true; - query.ParentId = queryParent.Id; - query.Genres = new[] { displayParent.Name }; - query.SetUser(user); - - query.IncludeItemTypes = new[] { typeof(MusicAlbum).Name }; - - return _libraryManager.GetItemsResult(query); - } - private QueryResult<BaseItem> GetMusicAlbumArtists(Folder parent, User user, InternalItemsQuery query) { var artists = _libraryManager.GetAlbumArtists(new InternalItemsQuery(user) @@ -1020,11 +1005,6 @@ namespace MediaBrowser.Controller.Entities return false; } - if (request.Studios.Length > 0) - { - return false; - } - if (request.StudioIds.Length > 0) { return false; @@ -1530,12 +1510,6 @@ namespace MediaBrowser.Controller.Entities } // Apply studio filter - if (query.Studios.Length > 0 && !query.Studios.Any(v => item.Studios.Contains(v, StringComparer.OrdinalIgnoreCase))) - { - return false; - } - - // Apply studio filter if (query.StudioIds.Length > 0 && !query.StudioIds.Any(id => { var studioItem = libraryManager.GetItemById(id); @@ -1748,14 +1722,14 @@ namespace MediaBrowser.Controller.Entities } // Artists - if (query.ArtistNames.Length > 0) + if (query.ArtistIds.Length > 0) { var audio = item as IHasArtist; - if (!(audio != null && query.ArtistNames.Any(audio.HasAnyArtist))) - { - return false; - } + //if (!(audio != null && query.ArtistNames.Any(audio.HasAnyArtist))) + //{ + // return false; + //} } // Albums diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 02e736d99..9087a6e1d 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -153,7 +153,7 @@ namespace MediaBrowser.Controller.Playlists : user.RootFolder.GetRecursiveChildren(user, new InternalItemsQuery(user) { IncludeItemTypes = new[] { typeof(Audio).Name }, - ArtistNames = new[] { musicArtist.Name } + ArtistIds = new[] { musicArtist.Id.ToString("N") } }); return LibraryManager.Sort(items, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending); diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 23e63dad0..116a6a7cf 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -95,6 +95,11 @@ namespace MediaBrowser.MediaEncoding.Encoder int defaultImageExtractionTimeoutMs, bool enableEncoderFontFile, IEnvironmentInfo environmentInfo) { + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } + _logger = logger; _jsonSerializer = jsonSerializer; ConfigurationManager = configurationManager; @@ -632,7 +637,7 @@ namespace MediaBrowser.MediaEncoding.Encoder var result = _jsonSerializer.DeserializeFromStream<InternalMediaInfoResult>(process.StandardOutput.BaseStream); - if (result.streams == null && result.format == null) + if (result == null || (result.streams == null && result.format == null)) { throw new Exception("ffprobe failed - streams and format are both null."); } diff --git a/MediaBrowser.Model/Entities/CollectionType.cs b/MediaBrowser.Model/Entities/CollectionType.cs index 187334c71..b1cbb9ac3 100644 --- a/MediaBrowser.Model/Entities/CollectionType.cs +++ b/MediaBrowser.Model/Entities/CollectionType.cs @@ -48,18 +48,10 @@ public const string MovieGenres = "MovieGenres"; public const string MovieGenre = "MovieGenre"; - public const string LatestGames = "LatestGames"; - public const string RecentlyPlayedGames = "RecentlyPlayedGames"; - public const string GameSystems = "GameSystems"; - public const string GameGenres = "GameGenres"; - public const string GameFavorites = "GameFavorites"; - public const string GameGenre = "GameGenre"; - public const string MusicArtists = "MusicArtists"; public const string MusicAlbumArtists = "MusicAlbumArtists"; public const string MusicAlbums = "MusicAlbums"; public const string MusicGenres = "MusicGenres"; - public const string MusicGenre = "MusicGenre"; public const string MusicLatest = "MusicLatest"; public const string MusicPlaylists = "MusicPlaylists"; public const string MusicSongs = "MusicSongs"; diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index 4992675da..538512557 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -238,7 +238,7 @@ namespace MediaBrowser.Providers.TV var targetSeries = DetermineAppropriateSeries(series, tuple.Item1); var seasonOffset = TvdbSeriesProvider.GetSeriesOffset(targetSeries.ProviderIds) ?? ((targetSeries.AnimeSeriesIndex ?? 1) - 1); - var unairedThresholdDays = 1; + var unairedThresholdDays = 2; now = now.AddDays(0 - unairedThresholdDays); if (airDate.Value < now) diff --git a/MediaBrowser.Server.Mac/Main.cs b/MediaBrowser.Server.Mac/Main.cs index cda6f459a..6d02c7a1a 100644 --- a/MediaBrowser.Server.Mac/Main.cs +++ b/MediaBrowser.Server.Mac/Main.cs @@ -142,7 +142,10 @@ namespace MediaBrowser.Server.Mac private static EnvironmentInfo GetEnvironmentInfo() { - var info = new EnvironmentInfo(); + var info = new EnvironmentInfo() + { + CustomOperatingSystem = MediaBrowser.Model.System.OperatingSystem.OSX + }; var uname = GetUnixName(); diff --git a/MediaBrowser.Server.Mono/MonoAppHost.cs b/MediaBrowser.Server.Mono/MonoAppHost.cs index 8def1ca2b..932e2d6cd 100644 --- a/MediaBrowser.Server.Mono/MonoAppHost.cs +++ b/MediaBrowser.Server.Mono/MonoAppHost.cs @@ -51,8 +51,11 @@ namespace MediaBrowser.Server.Mono } else if (environment.OperatingSystem == Model.System.OperatingSystem.Linux) { + info.FFMpegFilename = "ffmpeg"; + info.FFProbeFilename = "ffprobe"; info.ArchiveType = "7z"; info.Version = "20160215"; + info.DownloadUrls = GetDownloadUrls(); } // No version available - user requirement @@ -61,6 +64,25 @@ namespace MediaBrowser.Server.Mono return info; } + private string[] GetDownloadUrls() + { + switch (EnvironmentInfo.SystemArchitecture) + { + case Architecture.X64: + return new[] + { + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z" + }; + case Architecture.X86: + return new[] + { + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-32bit-static.7z" + }; + } + + return new string[] { }; + } + protected override void RestartInternal() { MainClass.Restart(StartupOptions); diff --git a/SharedVersion.cs b/SharedVersion.cs index 0561738e2..16c597910 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -//[assembly: AssemblyVersion("3.1.*")] -[assembly: AssemblyVersion("3.1.259")] +[assembly: AssemblyVersion("3.1.*")] +//[assembly: AssemblyVersion("3.1.259")] diff --git a/SocketHttpListener.Portable/Net/HttpConnection.cs b/SocketHttpListener.Portable/Net/HttpConnection.cs index bc4286dc8..3baffd258 100644 --- a/SocketHttpListener.Portable/Net/HttpConnection.cs +++ b/SocketHttpListener.Portable/Net/HttpConnection.cs @@ -209,7 +209,7 @@ namespace SocketHttpListener.Net // TODO: can we get this stream before reading the input? if (o_stream == null) { - context.Response.DetermineIfChunked(); + //context.Response.DetermineIfChunked(); if (context.Response.SendChunked || isExpect100Continue || context.Request.IsWebSocketRequest || true) { @@ -508,7 +508,7 @@ namespace SocketHttpListener.Net { force_close |= !context.Request.KeepAlive; if (!force_close) - force_close = (context.Response.Headers["connection"] == "close"); + force_close = (string.Equals(context.Response.Headers["connection"], "close", StringComparison.OrdinalIgnoreCase)); /* if (!force_close) { // bool conn_close = (status_code == 400 || status_code == 408 || status_code == 411 || diff --git a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs index c1182de34..9a5862cb9 100644 --- a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs +++ b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs @@ -386,7 +386,7 @@ namespace SocketHttpListener.Net if (content_type != null) { - if (content_encoding != null && content_type.IndexOf("charset=", StringComparison.Ordinal) == -1) + if (content_encoding != null && content_type.IndexOf("charset=", StringComparison.OrdinalIgnoreCase) == -1) { string enc_name = content_encoding.WebName; headers.SetInternal("Content-Type", content_type + "; charset=" + enc_name); @@ -429,9 +429,10 @@ namespace SocketHttpListener.Net * HttpStatusCode.InternalServerError 500 * HttpStatusCode.ServiceUnavailable 503 */ - bool conn_close = (status_code == 408 || status_code == 411 || + bool conn_close = status_code == 400 || status_code == 408 || status_code == 411 || status_code == 413 || status_code == 414 || - status_code == 503); + status_code == 500 || + status_code == 503; if (conn_close == false) conn_close = !context.Request.KeepAlive; diff --git a/SocketHttpListener.Portable/Net/ResponseStream.cs b/SocketHttpListener.Portable/Net/ResponseStream.cs index 6067a89ec..a79a18791 100644 --- a/SocketHttpListener.Portable/Net/ResponseStream.cs +++ b/SocketHttpListener.Portable/Net/ResponseStream.cs @@ -136,6 +136,11 @@ namespace SocketHttpListener.Net if (disposed) throw new ObjectDisposedException(GetType().ToString()); + if (count == 0) + { + //return; + } + byte[] bytes = null; MemoryStream ms = GetHeaders(response, _memoryStreamFactory, false); bool chunked = response.SendChunked; @@ -176,6 +181,11 @@ namespace SocketHttpListener.Net if (disposed) throw new ObjectDisposedException(GetType().ToString()); + if (count == 0) + { + //return; + } + byte[] bytes = null; MemoryStream ms = GetHeaders(response, _memoryStreamFactory, false); bool chunked = response.SendChunked; @@ -206,7 +216,7 @@ namespace SocketHttpListener.Net await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); } - if (response.SendChunked) + if (chunked) stream.Write(crlf, 0, 2); } |
