From 28c252921fac52685b01d91c965115e354f25bc6 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 19 Nov 2013 11:44:11 -0500 Subject: [PATCH 01/67] fix specials query --- MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs | 2 +- MediaBrowser.Api/UserLibrary/ItemsService.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs b/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs index 6c1aa04cfb..ee313ea3bb 100644 --- a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs +++ b/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs @@ -650,7 +650,7 @@ namespace MediaBrowser.Api.DefaultTheme public static IEnumerable Randomize(this IEnumerable sequence, string type = "none") where T : BaseItem { - var hour = DateTime.Now.Hour + 2; + var hour = DateTime.Now.Hour + DateTime.Now.Day + 2; var typeCode = type.GetHashCode(); diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 1c7722e587..c68e83047f 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -1013,7 +1013,7 @@ namespace MediaBrowser.Api.UserLibrary if (episode != null) { - var seasonNumber = episode.AirsAfterSeasonNumber ?? episode.AirsBeforeEpisodeNumber ?? episode.ParentIndexNumber; + var seasonNumber = episode.AirsAfterSeasonNumber ?? episode.AirsBeforeSeasonNumber ?? episode.ParentIndexNumber; return episode.PremiereDate.HasValue && seasonNumber.HasValue && seasonNumber.Value == val; } From 2b68dcd3c6903ce562beef4f0bb4dc02b4444ad8 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 19 Nov 2013 11:44:53 -0500 Subject: [PATCH 02/67] fix disc image saving for xbmc --- .../Providers/ImageSaver.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs b/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs index ff11b9a2be..4184003114 100644 --- a/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs +++ b/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs @@ -344,6 +344,9 @@ namespace MediaBrowser.Server.Implementations.Providers case ImageType.Art: filename = "clearart"; break; + case ImageType.Disc: + filename = item is MusicAlbum ? "cdart" : "disc"; + break; case ImageType.Primary: filename = item is Episode ? Path.GetFileNameWithoutExtension(item.Path) : "folder"; break; @@ -478,6 +481,11 @@ namespace MediaBrowser.Server.Implementations.Providers if (type == ImageType.Primary) { + if (item is MusicAlbum || item is Artist || item is MusicArtist) + { + return new[] { Path.Combine(item.Path, "folder" + extension) }; + } + if (item is Season && item.IndexNumber.HasValue) { var seriesFolder = Path.GetDirectoryName(item.Path); From de339b8265f0e0da7771b02cb9d00c40dad0163f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 19 Nov 2013 12:17:14 -0500 Subject: [PATCH 03/67] fixes #632 - Show yesterday's episodes in upcoming view --- MediaBrowser.Api/UserLibrary/ItemsService.cs | 23 ++++++++++++++++++- MediaBrowser.Model/Querying/ItemQuery.cs | 4 ++++ .../Providers/ImageSaver.cs | 13 +++++------ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index c68e83047f..4239869a52 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Dto; +using System.Globalization; +using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; @@ -205,6 +206,12 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "AiredDuringSeason", Description = "Gets all episodes that aired during a season, including specials.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? AiredDuringSeason { get; set; } + + [ApiMember(Name = "MinPremiereDate", Description = "Optional. The minimum premiere date. Format = yyyyMMddHHmmss", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public string MinPremiereDate { get; set; } + + [ApiMember(Name = "MaxPremiereDate", Description = "Optional. The maximum premiere date. Format = yyyyMMddHHmmss", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public string MaxPremiereDate { get; set; } } /// @@ -1022,6 +1029,20 @@ namespace MediaBrowser.Api.UserLibrary }); } + if (!string.IsNullOrEmpty(request.MinPremiereDate)) + { + var date = DateTime.ParseExact(request.MinPremiereDate, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); + + items = items.Where(i => i.PremiereDate.HasValue && i.PremiereDate.Value >= date); + } + + if (!string.IsNullOrEmpty(request.MaxPremiereDate)) + { + var date = DateTime.ParseExact(request.MaxPremiereDate, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); + + items = items.Where(i => i.PremiereDate.HasValue && i.PremiereDate.Value <= date); + } + return items; } diff --git a/MediaBrowser.Model/Querying/ItemQuery.cs b/MediaBrowser.Model/Querying/ItemQuery.cs index 14c946ba18..6602e031f5 100644 --- a/MediaBrowser.Model/Querying/ItemQuery.cs +++ b/MediaBrowser.Model/Querying/ItemQuery.cs @@ -266,6 +266,10 @@ namespace MediaBrowser.Model.Querying public double? MinCriticRating { get; set; } public int? AiredDuringSeason { get; set; } + + public DateTime? MinPremiereDate { get; set; } + + public DateTime? MaxPremiereDate { get; set; } /// /// Initializes a new instance of the class. diff --git a/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs b/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs index 4184003114..d9ee9e219a 100644 --- a/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs +++ b/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs @@ -481,11 +481,6 @@ namespace MediaBrowser.Server.Implementations.Providers if (type == ImageType.Primary) { - if (item is MusicAlbum || item is Artist || item is MusicArtist) - { - return new[] { Path.Combine(item.Path, "folder" + extension) }; - } - if (item is Season && item.IndexNumber.HasValue) { var seriesFolder = Path.GetDirectoryName(item.Path); @@ -513,8 +508,12 @@ namespace MediaBrowser.Server.Implementations.Providers return new[] { GetSavePathForItemInMixedFolder(item, type, string.Empty, extension) }; } - var filename = "poster" + extension; - return new[] { Path.Combine(item.MetaLocation, filename) }; + if (item is MusicAlbum || item is Artist || item is MusicArtist) + { + return new[] { Path.Combine(item.MetaLocation, "folder" + extension) }; + } + + return new[] { Path.Combine(item.MetaLocation, "poster" + extension) }; } if (type == ImageType.Banner) From bce86c502206b016cc448afc1934101b852a1994 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 19 Nov 2013 22:15:48 -0500 Subject: [PATCH 04/67] pull person sort order from tvdb/tmdb data --- MediaBrowser.Api/ItemUpdateService.cs | 7 +++++- MediaBrowser.Api/LibraryService.cs | 23 ++++++++++++------- .../UserLibrary/PersonsService.cs | 2 +- MediaBrowser.Controller/Entities/BaseItem.cs | 21 +++++++++++++---- .../Entities/IHasAspectRatio.cs | 14 +++++++++++ MediaBrowser.Controller/Entities/Person.cs | 6 +++++ MediaBrowser.Controller/Entities/Video.cs | 8 ++++++- .../LiveTv/ILiveTvService.cs | 11 ++++++++- .../MediaBrowser.Controller.csproj | 1 + .../Providers/BaseItemXmlParser.cs | 5 ++-- .../Movies/MovieDbProvider.cs | 2 +- .../Savers/XmlSaverHelpers.cs | 8 +++++-- .../TV/TvdbSeriesProvider.cs | 17 ++++++++++++++ .../Dto/DtoService.cs | 8 +++++-- 14 files changed, 109 insertions(+), 24 deletions(-) create mode 100644 MediaBrowser.Controller/Entities/IHasAspectRatio.cs diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 6e1dbc08b9..cfbaf70c90 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -247,11 +247,16 @@ namespace MediaBrowser.Api item.PremiereDate = request.PremiereDate.HasValue ? request.PremiereDate.Value.ToUniversalTime() : (DateTime?)null; item.ProductionYear = request.ProductionYear; item.ProductionLocations = request.ProductionLocations; - item.AspectRatio = request.AspectRatio; item.Language = request.Language; item.OfficialRating = request.OfficialRating; item.CustomRating = request.CustomRating; + var hasAspectRatio = item as IHasAspectRatio; + if (hasAspectRatio != null) + { + hasAspectRatio.AspectRatio = request.AspectRatio; + } + item.DontFetchMeta = !(request.EnableInternetProviders ?? true); if (request.EnableInternetProviders ?? true) { diff --git a/MediaBrowser.Api/LibraryService.cs b/MediaBrowser.Api/LibraryService.cs index 582eb6f497..06b90b32cb 100644 --- a/MediaBrowser.Api/LibraryService.cs +++ b/MediaBrowser.Api/LibraryService.cs @@ -681,6 +681,11 @@ namespace MediaBrowser.Api { var album = originalItem as MusicAlbum; + if (album == null) + { + album = originalItem.Parents.OfType().FirstOrDefault(); + } + if (album != null) { var linkedItemWithThemes = album.SoundtrackIds @@ -744,17 +749,12 @@ namespace MediaBrowser.Api : (Folder)_libraryManager.RootFolder) : _dtoService.GetItemByDtoId(id, userId); - while (GetSoundtrackSongIds(item).Count == 0 && inheritFromParent && item.Parent != null) - { - item = item.Parent; - } - // Get everything var fields = Enum.GetNames(typeof(ItemFields)) .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)) .ToList(); - var dtos = GetSoundtrackSongIds(item) + var dtos = GetSoundtrackSongIds(item, inheritFromParent) .Select(_libraryManager.GetItemById) .OfType() .SelectMany(i => i.RecursiveChildren) @@ -772,7 +772,7 @@ namespace MediaBrowser.Api }; } - private List GetSoundtrackSongIds(BaseItem item) + private IEnumerable GetSoundtrackSongIds(BaseItem item, bool inherit) { var hasSoundtracks = item as IHasSoundtracks; @@ -781,7 +781,14 @@ namespace MediaBrowser.Api return hasSoundtracks.SoundtrackIds; } - return new List(); + if (!inherit) + { + return null; + } + + hasSoundtracks = item.Parents.OfType().FirstOrDefault(); + + return hasSoundtracks != null ? hasSoundtracks.SoundtrackIds : new List(); } } } diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs index 885a4a3e67..09b5ef09fe 100644 --- a/MediaBrowser.Api/UserLibrary/PersonsService.cs +++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs @@ -155,7 +155,7 @@ namespace MediaBrowser.Api.UserLibrary /// IEnumerable{PersonInfo}. private IEnumerable GetAllPeople(IEnumerable itemsList, string[] personTypes) { - var people = itemsList.SelectMany(i => i.People.OrderBy(p => p.Type)); + var people = itemsList.SelectMany(i => i.People.OrderBy(p => p.SortOrder ?? int.MaxValue).ThenBy(p => p.Type)); return personTypes.Length == 0 ? diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index a6178536c6..6b6719f013 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -483,6 +483,22 @@ namespace MediaBrowser.Controller.Entities [IgnoreDataMember] public Folder Parent { get; set; } + [IgnoreDataMember] + public IEnumerable Parents + { + get + { + var parent = Parent; + + while (parent != null) + { + yield return parent; + + parent = parent.Parent; + } + } + } + /// /// When the item first debuted. For movies this could be premiere date, episodes would be first aired /// @@ -630,11 +646,6 @@ namespace MediaBrowser.Controller.Entities /// The original run time ticks. public long? OriginalRunTimeTicks { get; set; } /// - /// Gets or sets the aspect ratio. - /// - /// The aspect ratio. - public string AspectRatio { get; set; } - /// /// Gets or sets the production year. /// /// The production year. diff --git a/MediaBrowser.Controller/Entities/IHasAspectRatio.cs b/MediaBrowser.Controller/Entities/IHasAspectRatio.cs new file mode 100644 index 0000000000..5aecf4eac1 --- /dev/null +++ b/MediaBrowser.Controller/Entities/IHasAspectRatio.cs @@ -0,0 +1,14 @@ +namespace MediaBrowser.Controller.Entities +{ + /// + /// Interface IHasAspectRatio + /// + public interface IHasAspectRatio + { + /// + /// Gets or sets the aspect ratio. + /// + /// The aspect ratio. + string AspectRatio { get; set; } + } +} diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs index e5cf48ad08..17b9d77413 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -49,6 +49,12 @@ namespace MediaBrowser.Controller.Entities /// The type. public string Type { get; set; } + /// + /// Gets or sets the sort order - ascending + /// + /// The sort order. + public int? SortOrder { get; set; } + /// /// Returns a that represents this instance. /// diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index e900dd77e9..6a27ed6906 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.Controller.Entities /// /// Class Video /// - public class Video : BaseItem, IHasMediaStreams + public class Video : BaseItem, IHasMediaStreams, IHasAspectRatio { public bool IsMultiPart { get; set; } @@ -65,6 +65,12 @@ namespace MediaBrowser.Controller.Entities return GetPlayableStreamFiles(Path); } + /// + /// Gets or sets the aspect ratio. + /// + /// The aspect ratio. + public string AspectRatio { get; set; } + /// /// Should be overridden to return the proper folder where metadata lives /// diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs index 5c019ae8c4..d39d98fa3c 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.LiveTv; +using System.IO; +using MediaBrowser.Model.LiveTv; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -23,6 +24,14 @@ namespace MediaBrowser.Controller.LiveTv /// Task{IEnumerable{ChannelInfo}}. Task> GetChannelsAsync(CancellationToken cancellationToken); + /// + /// Gets the channel image asynchronous. + /// + /// The channel identifier. + /// The cancellation token. + /// Task{Stream}. + Task GetChannelImageAsync(string channelId, CancellationToken cancellationToken); + /// /// Gets the recordings asynchronous. /// diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index f2837a1f1d..8837d04f5e 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -89,6 +89,7 @@ + diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index 9fdbbf3b7e..d3fa7b09b1 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -375,9 +375,10 @@ namespace MediaBrowser.Controller.Providers { var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) + var hasAspectRatio = item as IHasAspectRatio; + if (!string.IsNullOrWhiteSpace(val) && hasAspectRatio != null) { - item.AspectRatio = val; + hasAspectRatio.AspectRatio = val; } break; } diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index cc6e07d622..f493353399 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -845,7 +845,7 @@ namespace MediaBrowser.Providers.Movies //actors come from cast if (movieData.casts != null && movieData.casts.cast != null) { - foreach (var actor in movieData.casts.cast.OrderBy(a => a.order)) movie.AddPerson(new PersonInfo { Name = actor.name.Trim(), Role = actor.character, Type = PersonType.Actor }); + foreach (var actor in movieData.casts.cast.OrderBy(a => a.order)) movie.AddPerson(new PersonInfo { Name = actor.name.Trim(), Role = actor.character, Type = PersonType.Actor, SortOrder = actor.order }); } //and the rest from crew diff --git a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs index 69276e0b8e..7c8d0431b2 100644 --- a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs +++ b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs @@ -301,9 +301,13 @@ namespace MediaBrowser.Providers.Savers builder.Append("" + SecurityElement.Escape(item.HomePageUrl) + ""); } - if (!string.IsNullOrEmpty(item.AspectRatio)) + var hasAspectRatio = item as IHasAspectRatio; + if (hasAspectRatio != null) { - builder.Append("" + SecurityElement.Escape(item.AspectRatio) + ""); + if (!string.IsNullOrEmpty(hasAspectRatio.AspectRatio)) + { + builder.Append("" + SecurityElement.Escape(hasAspectRatio.AspectRatio) + ""); + } } if (!string.IsNullOrEmpty(item.Language)) diff --git a/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs index a22f4f1c32..29e191a594 100644 --- a/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs @@ -1056,6 +1056,23 @@ namespace MediaBrowser.Providers.TV break; } + case "SortOrder": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval)) + { + personInfo.SortOrder = rval; + } + } + break; + } + default: reader.Skip(); break; diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 1f0e7d1e1e..4efafcd772 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -433,7 +433,7 @@ namespace MediaBrowser.Server.Implementations.Dto // Ordering by person type to ensure actors and artists are at the front. // This is taking advantage of the fact that they both begin with A // This should be improved in the future - var people = item.People.OrderBy(i => i.Type).ToList(); + var people = item.People.OrderBy(i => i.SortOrder ?? int.MaxValue).ThenBy(i => i.Type).ToList(); // Attach People by transforming them into BaseItemPerson (DTO) dto.People = new BaseItemPerson[people.Count]; @@ -760,7 +760,11 @@ namespace MediaBrowser.Server.Implementations.Dto dto.ProductionLocations = item.ProductionLocations; } - dto.AspectRatio = item.AspectRatio; + var hasAspectRatio = item as IHasAspectRatio; + if (hasAspectRatio != null) + { + dto.AspectRatio = hasAspectRatio.AspectRatio; + } dto.BackdropImageTags = GetBackdropImageTags(item); From 6ee94ee1a245ef4809620c3f0c8d1d787c932581 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 19 Nov 2013 22:47:29 -0500 Subject: [PATCH 05/67] store person sort order in xml --- MediaBrowser.Controller/Entities/BaseItem.cs | 18 ++++++++++++++++-- .../Providers/BaseItemXmlParser.cs | 17 ++++++++++++++++- .../Savers/XmlSaverHelpers.cs | 6 ++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 6b6719f013..b139cba9a3 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1198,6 +1198,7 @@ namespace MediaBrowser.Controller.Entities if (existing != null) { existing.Type = PersonType.GuestStar; + existing.SortOrder = person.SortOrder ?? existing.SortOrder; return; } } @@ -1214,16 +1215,29 @@ namespace MediaBrowser.Controller.Entities else { // Was there, if no role and we have one - fill it in - if (string.IsNullOrWhiteSpace(existing.Role) && !string.IsNullOrWhiteSpace(person.Role)) existing.Role = person.Role; + if (string.IsNullOrWhiteSpace(existing.Role) && !string.IsNullOrWhiteSpace(person.Role)) + { + existing.Role = person.Role; + } + + existing.SortOrder = person.SortOrder ?? existing.SortOrder; } } else { + var existing = People.FirstOrDefault(p => + string.Equals(p.Name, person.Name, StringComparison.OrdinalIgnoreCase) && + string.Equals(p.Type, person.Type, StringComparison.OrdinalIgnoreCase)); + // Check for dupes based on the combination of Name and Type - if (!People.Any(p => string.Equals(p.Name, person.Name, StringComparison.OrdinalIgnoreCase) && string.Equals(p.Type, person.Type, StringComparison.OrdinalIgnoreCase))) + if (existing == null) { People.Add(person); } + else + { + existing.SortOrder = person.SortOrder ?? existing.SortOrder; + } } } diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index d3fa7b09b1..0856ca1f61 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -1075,6 +1075,7 @@ namespace MediaBrowser.Controller.Providers var names = new List(); var type = "Actor"; // If type is not specified assume actor var role = string.Empty; + int? sortOrder = null; reader.MoveToContent(); @@ -1109,6 +1110,20 @@ namespace MediaBrowser.Controller.Providers } break; } + case "SortOrder": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int intVal; + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out intVal)) + { + sortOrder = intVal; + } + } + break; + } default: reader.Skip(); @@ -1117,7 +1132,7 @@ namespace MediaBrowser.Controller.Providers } } - return names.Select(n => new PersonInfo { Name = n.Trim(), Role = role, Type = type }); + return names.Select(n => new PersonInfo { Name = n.Trim(), Role = role, Type = type, SortOrder = sortOrder }); } /// diff --git a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs index 7c8d0431b2..a0c830db4a 100644 --- a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs +++ b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs @@ -463,6 +463,12 @@ namespace MediaBrowser.Providers.Savers builder.Append("" + SecurityElement.Escape(person.Name) + ""); builder.Append("" + SecurityElement.Escape(person.Type) + ""); builder.Append("" + SecurityElement.Escape(person.Role) + ""); + + if (person.SortOrder.HasValue) + { + builder.Append("" + SecurityElement.Escape(person.SortOrder.Value.ToString(UsCulture)) + ""); + } + builder.Append(""); } From f7271792222dfa6f4cf4e5b3c5d8876775f88e2b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 20 Nov 2013 10:37:21 -0500 Subject: [PATCH 06/67] fixes #631 - FFProbe Bluray Folder Rip --- .../MediaInfo/BaseFFProbeProvider.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs index a843103106..8ad2761cd9 100644 --- a/MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs @@ -228,14 +228,21 @@ namespace MediaBrowser.Providers.MediaInfo // Get stream bitrate if (stream.Type != MediaStreamType.Subtitle) { + var bitrate = 0; + if (!string.IsNullOrEmpty(streamInfo.bit_rate)) { - stream.BitRate = int.Parse(streamInfo.bit_rate, UsCulture); + bitrate = int.Parse(streamInfo.bit_rate, UsCulture); } else if (formatInfo != null && !string.IsNullOrEmpty(formatInfo.bit_rate)) { // If the stream info doesn't have a bitrate get the value from the media format info - stream.BitRate = int.Parse(formatInfo.bit_rate, UsCulture); + bitrate = int.Parse(formatInfo.bit_rate, UsCulture); + } + + if (bitrate > 0) + { + stream.BitRate = bitrate; } } From 7dd75e079a7c2c82e00bb84bed0c7308ba738e3c Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 20 Nov 2013 10:50:54 -0500 Subject: [PATCH 07/67] fixes #619 - Command line fail for internal subs --- .../Playback/BaseStreamingService.cs | 7 ++--- .../Playback/Hls/VideoHlsService.cs | 28 +++++++++++-------- .../Playback/Progressive/VideoService.cs | 20 +++++++------ MediaBrowser.Controller/Entities/BaseItem.cs | 1 - 4 files changed, 31 insertions(+), 25 deletions(-) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 4a767b0889..67d9cf7a47 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1,5 +1,4 @@ -using System.Globalization; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller; @@ -11,15 +10,15 @@ using MediaBrowser.Controller.MediaInfo; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; using System; using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.IO; namespace MediaBrowser.Api.Playback { diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 080ab9c7ed..dfd18ab154 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -88,7 +88,7 @@ namespace MediaBrowser.Api.Playback.Hls } var volParam = string.Empty; - var AudioSampleRate = string.Empty; + var audioSampleRate = string.Empty; // Boost volume to 200% when downsampling from 6ch to 2ch if (channels.HasValue && channels.Value <= 2 && state.AudioStream.Channels.HasValue && state.AudioStream.Channels.Value > 5) @@ -98,10 +98,10 @@ namespace MediaBrowser.Api.Playback.Hls if (state.Request.AudioSampleRate.HasValue) { - AudioSampleRate= state.Request.AudioSampleRate.Value + ":"; + audioSampleRate= state.Request.AudioSampleRate.Value + ":"; } - args += string.Format(" -af \"adelay=1,aresample={0}async=1000{1}\"",AudioSampleRate, volParam); + args += string.Format(" -af \"adelay=1,aresample={0}async=1000{1}\"",audioSampleRate, volParam); return args; } @@ -127,6 +127,10 @@ namespace MediaBrowser.Api.Playback.Hls const string keyFrameArg = " -force_key_frames expr:if(isnan(prev_forced_t),gte(t,.1),gte(t,prev_forced_t+5))"; + var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsExternal && + (state.SubtitleStream.Codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) != -1 || + state.SubtitleStream.Codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1); + var args = "-codec:v:0 " + codec + " -preset superfast" + keyFrameArg; var bitrate = GetVideoBitrateParam(state); @@ -137,9 +141,12 @@ namespace MediaBrowser.Api.Playback.Hls } // Add resolution params, if specified - if (state.VideoRequest.Width.HasValue || state.VideoRequest.Height.HasValue || state.VideoRequest.MaxHeight.HasValue || state.VideoRequest.MaxWidth.HasValue) + if (!hasGraphicalSubs) { - args += GetOutputSizeParam(state, codec, performSubtitleConversion); + if (state.VideoRequest.Width.HasValue || state.VideoRequest.Height.HasValue || state.VideoRequest.MaxHeight.HasValue || state.VideoRequest.MaxWidth.HasValue) + { + args += GetOutputSizeParam(state, codec, performSubtitleConversion); + } } if (state.VideoRequest.Framerate.HasValue) @@ -158,14 +165,11 @@ namespace MediaBrowser.Api.Playback.Hls { args += " -level " + state.VideoRequest.Level; } - - if (state.SubtitleStream != null) + + // This is for internal graphical subs + if (hasGraphicalSubs) { - // This is for internal graphical subs - if (!state.SubtitleStream.IsExternal && (state.SubtitleStream.Codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) != -1 || state.SubtitleStream.Codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1)) - { - args += GetInternalGraphicalSubtitleParam(state, codec); - } + args += GetInternalGraphicalSubtitleParam(state, codec); } return args; diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 0ef6d13ccc..97b808b867 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -143,12 +143,19 @@ namespace MediaBrowser.Api.Playback.Progressive args += keyFrameArg; + var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsExternal && + (state.SubtitleStream.Codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) != -1 || + state.SubtitleStream.Codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1); + var request = state.VideoRequest; // Add resolution params, if specified - if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue) + if (!hasGraphicalSubs) { - args += GetOutputSizeParam(state, codec, performSubtitleConversion); + if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue) + { + args += GetOutputSizeParam(state, codec, performSubtitleConversion); + } } if (request.Framerate.HasValue) @@ -175,13 +182,10 @@ namespace MediaBrowser.Api.Playback.Progressive args += " -level " + state.VideoRequest.Level; } - if (state.SubtitleStream != null) + // This is for internal graphical subs + if (hasGraphicalSubs) { - // This is for internal graphical subs - if (!state.SubtitleStream.IsExternal && (state.SubtitleStream.Codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) != -1 || state.SubtitleStream.Codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1)) - { - args += GetInternalGraphicalSubtitleParam(state, codec); - } + args += GetInternalGraphicalSubtitleParam(state, codec); } return args; diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index b139cba9a3..29765fe126 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1244,7 +1244,6 @@ namespace MediaBrowser.Controller.Entities /// /// Adds the tagline. /// - /// The item. /// The tagline. /// tagline public void AddTagline(string tagline) From 977d9c7f3b92154642046f06c8ac2c4dfb31d35e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 20 Nov 2013 12:10:02 -0500 Subject: [PATCH 08/67] improve episode sorting with embedded specials --- .../LiveTv/ILiveTvService.cs | 21 ++- MediaBrowser.Model/Querying/ItemSortBy.cs | 1 + .../TV/EpisodeIndexNumberProvider.cs | 26 ++++ ...MediaBrowser.Server.Implementations.csproj | 1 + .../Sorting/AiredEpisodeOrderComparer.cs | 134 ++++++++++++++++++ 5 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs index d39d98fa3c..3d68a4ec71 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using MediaBrowser.Model.LiveTv; using System.Collections.Generic; using System.Threading; @@ -24,6 +25,24 @@ namespace MediaBrowser.Controller.LiveTv /// Task{IEnumerable{ChannelInfo}}. Task> GetChannelsAsync(CancellationToken cancellationToken); + /// + /// Cancels the recording asynchronous. + /// + /// The recording identifier. + /// The cancellation token. + /// Task. + Task CancelRecordingAsync(string recordingId, CancellationToken cancellationToken); + + /// + /// Schedules the recording asynchronous. + /// + /// The channel identifier. + /// The start time. + /// The duration. + /// The cancellation token. + /// Task. + Task ScheduleRecordingAsync(string channelId, DateTime startTime, TimeSpan duration, CancellationToken cancellationToken); + /// /// Gets the channel image asynchronous. /// diff --git a/MediaBrowser.Model/Querying/ItemSortBy.cs b/MediaBrowser.Model/Querying/ItemSortBy.cs index 12dfa96261..57e09d724c 100644 --- a/MediaBrowser.Model/Querying/ItemSortBy.cs +++ b/MediaBrowser.Model/Querying/ItemSortBy.cs @@ -6,6 +6,7 @@ namespace MediaBrowser.Model.Querying /// public static class ItemSortBy { + public const string AiredEpisodeOrder = "AiredEpisodeOrder"; /// /// The album /// diff --git a/MediaBrowser.Providers/TV/EpisodeIndexNumberProvider.cs b/MediaBrowser.Providers/TV/EpisodeIndexNumberProvider.cs index fc8f55ae14..592c5dcac3 100644 --- a/MediaBrowser.Providers/TV/EpisodeIndexNumberProvider.cs +++ b/MediaBrowser.Providers/TV/EpisodeIndexNumberProvider.cs @@ -27,6 +27,22 @@ namespace MediaBrowser.Providers.TV { } + protected override bool RefreshOnVersionChange + { + get + { + return true; + } + } + + protected override string ProviderVersion + { + get + { + return "2"; + } + } + /// /// Supportses the specified item. /// @@ -51,6 +67,16 @@ namespace MediaBrowser.Providers.TV episode.IndexNumber = TVUtils.GetEpisodeNumberFromFile(item.Path, item.Parent is Season); episode.IndexNumberEnd = TVUtils.GetEndingEpisodeNumberFromFile(item.Path); + if (!episode.ParentIndexNumber.HasValue) + { + var season = episode.Parent as Season; + + if (season != null) + { + episode.ParentIndexNumber = season.IndexNumber; + } + } + SetLastRefreshed(item, DateTime.UtcNow); return TrueTaskResult; diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index ac451e1ebd..282991cc1f 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -188,6 +188,7 @@ + diff --git a/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs b/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs new file mode 100644 index 0000000000..cec9743bae --- /dev/null +++ b/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs @@ -0,0 +1,134 @@ +using System; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Sorting; +using MediaBrowser.Model.Querying; + +namespace MediaBrowser.Server.Implementations.Sorting +{ + class AiredEpisodeOrderComparer : IBaseItemComparer + { + /// + /// Compares the specified x. + /// + /// The x. + /// The y. + /// System.Int32. + public int Compare(BaseItem x, BaseItem y) + { + var val = DateTime.Compare(x.PremiereDate ?? DateTime.MinValue, y.PremiereDate ?? DateTime.MinValue); + + if (val != 0) + { + return val; + } + + var episode1 = x as Episode; + var episode2 = y as Episode; + + if (episode1 == null) + { + if (episode2 == null) + { + return 0; + } + + return 1; + } + + if (episode2 == null) + { + return -1; + } + + return Compare(episode1, episode2); + } + + private int Compare(Episode x, Episode y) + { + var isXSpecial = (x.ParentIndexNumber ?? -1) == 0; + var isYSpecial = (y.ParentIndexNumber ?? -1) == 0; + + if (isXSpecial && isYSpecial) + { + return CompareSpecials(x, y); + } + + if (!isXSpecial && !isYSpecial) + { + return CompareEpisodes(x, y); + } + + if (!isXSpecial && isYSpecial) + { + return CompareEpisodeToSpecial(x, y); + } + + return CompareEpisodeToSpecial(x, y) * -1; + } + + private int CompareEpisodeToSpecial(Episode x, Episode y) + { + var xSeason = x.ParentIndexNumber ?? -1; + var ySeason = y.AirsAfterSeasonNumber ?? y.AirsBeforeSeasonNumber ?? -1; + + if (xSeason != ySeason) + { + return xSeason.CompareTo(ySeason); + } + + // Now we know they have the same season + + // Compare episode number + + // Add 1 to to non-specials to account for AirsBeforeEpisodeNumber + var xEpisode = (x.IndexNumber ?? 0) * 1000 + 1; + var yEpisode = (y.AirsBeforeEpisodeNumber ?? 0) * 1000; + + return xEpisode.CompareTo(yEpisode); + } + + private int CompareSpecials(Episode x, Episode y) + { + return GetSpecialCompareValue(x).CompareTo(GetSpecialCompareValue(y)); + } + + private int GetSpecialCompareValue(Episode item) + { + // First sort by season number + // Since there are three sort orders, pad with 9 digits (3 for each, figure 1000 episode buffer should be enough) + var val = (item.AirsBeforeSeasonNumber ?? item.AirsAfterSeasonNumber ?? 0) * 1000000000; + + // Second sort order is if it airs after the season + if (item.AirsAfterSeasonNumber.HasValue) + { + val += 1000000; + } + + // Third level is the episode number + val += (item.AirsBeforeEpisodeNumber ?? 0) * 1000; + + // Finally, if that's still the same, last resort is the special number itself + val += item.IndexNumber ?? 0; + + return val; + } + + private int CompareEpisodes(Episode x, Episode y) + { + var xValue = ((x.ParentIndexNumber ?? -1) * 1000) + (x.IndexNumber ?? -1); + var yValue = ((y.ParentIndexNumber ?? -1) * 1000) + (y.IndexNumber ?? -1); + + return xValue.CompareTo(yValue); + } + + /// + /// Gets the name. + /// + /// The name. + public string Name + { + get { return ItemSortBy.AiredEpisodeOrder; } + } + } +} From 2f3b26ed30a460d0b4238547dce0821603784a9f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 20 Nov 2013 12:11:59 -0500 Subject: [PATCH 09/67] updated nuget --- Nuget/MediaBrowser.Common.Internal.nuspec | 4 ++-- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 4e6f0903c9..70922e14bf 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.243 + 3.0.244 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index e7c30692ca..16413baf5f 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.243 + 3.0.244 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 9cd0bf5471..7246f517fa 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.243 + 3.0.244 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - + From c3530e5e3255d2c2e8ec5fa4bc4635a21904235e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 20 Nov 2013 12:49:11 -0500 Subject: [PATCH 10/67] fixes #633 - Support Canadian ratings --- .../Localization/Ratings/ca.txt | 11 +++++++++++ .../MediaBrowser.Server.Implementations.csproj | 1 + 2 files changed, 12 insertions(+) create mode 100644 MediaBrowser.Server.Implementations/Localization/Ratings/ca.txt diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/ca.txt b/MediaBrowser.Server.Implementations/Localization/Ratings/ca.txt new file mode 100644 index 0000000000..e551223a8c --- /dev/null +++ b/MediaBrowser.Server.Implementations/Localization/Ratings/ca.txt @@ -0,0 +1,11 @@ +CA-G,1 +GB-U,1 +CA-PG,5 +DE-0,5 +CA-14A,7 +DE-12,7 +CA-A,8 +CA-18A,9 +SE-11,9 +DE-16,9 +CA-R,10 \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 282991cc1f..9347ea0ebe 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -266,6 +266,7 @@ + PreserveNewest From 71514af96be684dcbf432c5aca37712bbd773740 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 20 Nov 2013 16:08:12 -0500 Subject: [PATCH 11/67] render channels page --- MediaBrowser.Api/UserLibrary/ItemsService.cs | 55 +++++++++++++------ .../Sorting/AiredEpisodeOrderComparer.cs | 15 +++-- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 4239869a52..3936014c5c 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -206,7 +206,7 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "AiredDuringSeason", Description = "Gets all episodes that aired during a season, including specials.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? AiredDuringSeason { get; set; } - + [ApiMember(Name = "MinPremiereDate", Description = "Optional. The minimum premiere date. Format = yyyyMMddHHmmss", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] public string MinPremiereDate { get; set; } @@ -1012,21 +1012,7 @@ namespace MediaBrowser.Api.UserLibrary if (request.AiredDuringSeason.HasValue) { - var val = request.AiredDuringSeason.Value; - - items = items.Where(i => - { - var episode = i as Episode; - - if (episode != null) - { - var seasonNumber = episode.AirsAfterSeasonNumber ?? episode.AirsBeforeSeasonNumber ?? episode.ParentIndexNumber; - - return episode.PremiereDate.HasValue && seasonNumber.HasValue && seasonNumber.Value == val; - } - - return false; - }); + items = FilterByAiredDuringSeason(items, request.AiredDuringSeason.Value); } if (!string.IsNullOrEmpty(request.MinPremiereDate)) @@ -1046,6 +1032,43 @@ namespace MediaBrowser.Api.UserLibrary return items; } + private IEnumerable FilterByAiredDuringSeason(IEnumerable items, int seasonNumber) + { + var episodes = items.OfType().ToList(); + + // We can only enforce the air date requirement if the episodes have air dates + var enforceAirDate = episodes.Any(i => i.PremiereDate.HasValue); + + return episodes.Where(i => + { + var episode = i; + + if (episode != null) + { + var currentSeasonNumber = episode.AirsAfterSeasonNumber ?? episode.AirsBeforeSeasonNumber ?? episode.ParentIndexNumber; + + // If this produced nothing, try and get it from the parent folder + if (!currentSeasonNumber.HasValue) + { + var season = episode.Parent as Season; + if (season != null) + { + currentSeasonNumber = season.IndexNumber; + } + } + + if (enforceAirDate && !episode.PremiereDate.HasValue) + { + return false; + } + + return currentSeasonNumber.HasValue && currentSeasonNumber.Value == seasonNumber; + } + + return false; + }); + } + /// /// Determines whether the specified item has image. /// diff --git a/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs b/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs index cec9743bae..23334aa412 100644 --- a/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs +++ b/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs @@ -1,8 +1,8 @@ -using System; -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Sorting; using MediaBrowser.Model.Querying; +using System; namespace MediaBrowser.Server.Implementations.Sorting { @@ -16,11 +16,14 @@ namespace MediaBrowser.Server.Implementations.Sorting /// System.Int32. public int Compare(BaseItem x, BaseItem y) { - var val = DateTime.Compare(x.PremiereDate ?? DateTime.MinValue, y.PremiereDate ?? DateTime.MinValue); - - if (val != 0) + if (x.PremiereDate.HasValue && y.PremiereDate.HasValue) { - return val; + var val = DateTime.Compare(x.PremiereDate.Value, y.PremiereDate.Value); + + if (val != 0) + { + return val; + } } var episode1 = x as Episode; From bf5b4decb7ab1373fd59dfae7a4fba5631d61291 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 21 Nov 2013 11:02:31 -0500 Subject: [PATCH 12/67] check for zero bitrate coming from bdinfo --- MediaBrowser.Providers/Savers/MovieXmlSaver.cs | 3 ++- .../BdInfo/BdInfoExaminer.cs | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs index a974fc13e9..1a287a918f 100644 --- a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs @@ -119,7 +119,8 @@ namespace MediaBrowser.Providers.Savers "IMDBrating", "Description", "Artist", - "Album" + "Album", + "TmdbCollectionName" }); // Set last refreshed so that the provider doesn't trigger after the file save diff --git a/MediaBrowser.Server.Implementations/BdInfo/BdInfoExaminer.cs b/MediaBrowser.Server.Implementations/BdInfo/BdInfoExaminer.cs index 219b76cd53..06768f353b 100644 --- a/MediaBrowser.Server.Implementations/BdInfo/BdInfoExaminer.cs +++ b/MediaBrowser.Server.Implementations/BdInfo/BdInfoExaminer.cs @@ -127,7 +127,6 @@ namespace MediaBrowser.Server.Implementations.BdInfo { var stream = new MediaStream { - BitRate = Convert.ToInt32(audioStream.BitRate), Codec = audioStream.CodecShortName, Language = audioStream.LanguageCode, Channels = audioStream.ChannelCount, @@ -136,6 +135,13 @@ namespace MediaBrowser.Server.Implementations.BdInfo Index = streams.Count }; + var bitrate = Convert.ToInt32(audioStream.BitRate); + + if (bitrate > 0) + { + stream.BitRate = bitrate; + } + if (audioStream.LFE > 0) { stream.Channels = audioStream.ChannelCount + 1; From ee1a746031f15bdfc5c5e38fab704f5fcb9b67ee Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 21 Nov 2013 11:02:49 -0500 Subject: [PATCH 13/67] use string.equals --- .../Library/Resolvers/Movies/MovieResolver.cs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 0f87b9d337..07beabb832 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -345,18 +345,9 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies /// true if [is DVD directory] [the specified directory name]; otherwise, false. private bool IsDvdDirectory(string directoryName) { - return directoryName.Equals("video_ts", StringComparison.OrdinalIgnoreCase); + return string.Equals(directoryName, "video_ts", StringComparison.OrdinalIgnoreCase); } - /// - /// Determines whether [is hd DVD directory] [the specified directory name]. - /// - /// Name of the directory. - /// true if [is hd DVD directory] [the specified directory name]; otherwise, false. - private bool IsHdDvdDirectory(string directoryName) - { - return directoryName.Equals("hvdvd_ts", StringComparison.OrdinalIgnoreCase); - } /// /// Determines whether [is blu ray directory] [the specified directory name]. /// @@ -364,7 +355,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies /// true if [is blu ray directory] [the specified directory name]; otherwise, false. private bool IsBluRayDirectory(string directoryName) { - return directoryName.Equals("bdmv", StringComparison.OrdinalIgnoreCase); + return string.Equals(directoryName, "bdmv", StringComparison.OrdinalIgnoreCase); } } } From ef96c30ba55a3ab964df8d8bbf83ebb06bac5d39 Mon Sep 17 00:00:00 2001 From: Sven Van den brande Date: Thu, 21 Nov 2013 21:26:45 +0100 Subject: [PATCH 14/67] Added name to ScheduleRecordingAsync --- MediaBrowser.Controller/LiveTv/ILiveTvService.cs | 3 ++- MediaBrowser.sln | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs index 3d68a4ec71..03584240ee 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs @@ -36,12 +36,13 @@ namespace MediaBrowser.Controller.LiveTv /// /// Schedules the recording asynchronous. /// + /// The name for the recording /// The channel identifier. /// The start time. /// The duration. /// The cancellation token. /// Task. - Task ScheduleRecordingAsync(string channelId, DateTime startTime, TimeSpan duration, CancellationToken cancellationToken); + Task ScheduleRecordingAsync(string name,string channelId, DateTime startTime, TimeSpan duration, CancellationToken cancellationToken); /// /// Gets the channel image asynchronous. diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 0c5360b494..744debbcd2 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -237,7 +237,4 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(Performance) = preSolution - HasPerformanceSessions = true - EndGlobalSection EndGlobal From 17bacee0890cb03a579f9469e435d922bbdfdd50 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 21 Nov 2013 15:48:26 -0500 Subject: [PATCH 15/67] consolidate Artist & MusicArtist --- MediaBrowser.Api/BaseApiService.cs | 18 +--- .../DefaultTheme/DefaultThemeService.cs | 15 +-- MediaBrowser.Api/ItemRefreshService.cs | 14 +-- MediaBrowser.Api/ItemUpdateService.cs | 9 -- MediaBrowser.Api/LibraryService.cs | 16 +--- MediaBrowser.Api/SearchService.cs | 6 +- .../UserLibrary/ArtistsService.cs | 21 +--- .../Entities/Audio/Artist.cs | 86 ----------------- .../Entities/Audio/MusicArtist.cs | 69 ++++++++++++- MediaBrowser.Controller/Entities/BaseItem.cs | 5 + MediaBrowser.Controller/Entities/Folder.cs | 20 ++-- .../Entities/IItemByName.cs | 5 + .../Library/ILibraryManager.cs | 15 ++- .../MediaBrowser.Controller.csproj | 1 - .../FolderProviderFromXml.cs | 2 +- .../MediaBrowser.Providers.csproj | 2 - .../MediaInfo/BaseFFProbeProvider.cs | 4 +- .../Music/ArtistInfoFromSongProvider.cs | 42 ++++---- .../Music/ArtistProviderFromXml.cs | 13 +-- .../Music/FanArtArtistByNameProvider.cs | 47 --------- .../Music/LastFmImageProvider.cs | 2 +- .../Music/LastfmArtistByNameProvider.cs | 89 ----------------- .../Music/LastfmArtistProvider.cs | 11 --- MediaBrowser.Providers/Music/LastfmHelper.cs | 20 +--- .../Music/ManualFanartArtistProvider.cs | 2 +- .../Music/ManualLastFmImageProvider.cs | 9 +- .../Savers/ArtistXmlSaver.cs | 3 +- .../Savers/FolderXmlSaver.cs | 2 +- .../Dto/DtoService.cs | 4 +- .../Library/LibraryManager.cs | 96 +++++++++++++++---- .../Library/LuceneSearchEngine.cs | 16 +--- .../Library/Validators/ArtistsValidator.cs | 73 ++------------ .../Providers/ImageSaver.cs | 2 +- .../Api/DashboardService.cs | 1 + .../MediaBrowser.WebDashboard.csproj | 6 ++ 35 files changed, 246 insertions(+), 500 deletions(-) delete mode 100644 MediaBrowser.Controller/Entities/Audio/Artist.cs delete mode 100644 MediaBrowser.Providers/Music/FanArtArtistByNameProvider.cs delete mode 100644 MediaBrowser.Providers/Music/LastfmArtistByNameProvider.cs diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index d01e96a5ad..ee0721d5eb 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -92,7 +92,7 @@ namespace MediaBrowser.Api private readonly char[] _dashReplaceChars = new[] { '?', '/' }; private const char SlugChar = '-'; - protected Artist GetArtist(string name, ILibraryManager libraryManager) + protected MusicArtist GetArtist(string name, ILibraryManager libraryManager) { return libraryManager.GetArtist(DeSlugArtistName(name, libraryManager)); } @@ -147,21 +147,7 @@ namespace MediaBrowser.Api return name; } - return libraryManager.RootFolder.GetRecursiveChildren() - .OfType /// The original air date. public DateTime? OriginalAirDate { get; set; } + + /// + /// Gets or sets the recording identifier. + /// + /// The recording identifier. + public string RecordingId { get; set; } + + /// + /// Gets or sets the recording status. + /// + /// The recording status. + public RecordingStatus? RecordingStatus { get; set; } public ProgramInfoDto() { diff --git a/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs b/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs index 8b0a28ed0c..926198b93f 100644 --- a/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs +++ b/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs @@ -51,11 +51,6 @@ namespace MediaBrowser.Model.LiveTv /// public DateTime EndDate { get; set; } - /// - /// IsRecurring recording? - /// - public bool IsRecurring { get; set; } - /// /// Gets or sets the status. /// diff --git a/MediaBrowser.Model/LiveTv/RecordingQuery.cs b/MediaBrowser.Model/LiveTv/RecordingQuery.cs index 8d9866b5ec..8c83b0fcbd 100644 --- a/MediaBrowser.Model/LiveTv/RecordingQuery.cs +++ b/MediaBrowser.Model/LiveTv/RecordingQuery.cs @@ -5,6 +5,16 @@ /// public class RecordingQuery { + /// + /// Gets or sets the channel identifier. + /// + /// The channel identifier. public string ChannelId { get; set; } + + /// + /// Gets or sets the name of the service. + /// + /// The name of the service. + public string ServiceName { get; set; } } } diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index dd1e2b9fa6..5333518753 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -207,7 +207,7 @@ namespace MediaBrowser.Server.Implementations.Dto if (!string.IsNullOrEmpty(image)) { - dto.PrimaryImageTag = _imageProcessor.GetImageCacheTag(user, ImageType.Primary, image); + dto.PrimaryImageTag = GetImageCacheTag(user, ImageType.Primary, image); try { @@ -285,13 +285,7 @@ namespace MediaBrowser.Server.Implementations.Dto if (!string.IsNullOrEmpty(imagePath)) { - try - { - info.PrimaryImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary, imagePath); - } - catch (IOException) - { - } + info.PrimaryImageTag = GetImageCacheTag(item, ImageType.Primary, imagePath); } return info; diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index e48cea0f7c..12241876a1 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -41,9 +41,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv private List _channels = new List(); private List _programs = new List(); - private List _recordings = new List(); - - private readonly SemaphoreSlim _updateSemaphore = new SemaphoreSlim(1, 1); public LiveTvManager(IServerApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserManager userManager, ILocalizationManager localization, IUserDataManager userDataManager, IDtoService dtoService) { @@ -104,11 +101,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv return dto; } - private ILiveTvService GetService(ChannelInfo channel) - { - return _services.FirstOrDefault(i => string.Equals(channel.ServiceName, i.Name, StringComparison.OrdinalIgnoreCase)); - } - private Guid? GetLogoImageTag(Channel info) { var path = info.PrimaryImagePath; @@ -210,7 +202,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv Quality = program.Quality, OriginalAirDate = program.OriginalAirDate, Audio = program.Audio, - CommunityRating = program.CommunityRating + CommunityRating = program.CommunityRating, + AspectRatio = program.AspectRatio }; } @@ -228,9 +221,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv return name.ToLower().GetMD5(); } - private async Task GetChannel(ChannelInfo channelInfo, CancellationToken cancellationToken) + private async Task GetChannel(ChannelInfo channelInfo, string serviceName, CancellationToken cancellationToken) { - var path = Path.Combine(_appPaths.ItemsByNamePath, "channels", _fileSystem.GetValidFilename(channelInfo.ServiceName), _fileSystem.GetValidFilename(channelInfo.Name)); + var path = Path.Combine(_appPaths.ItemsByNamePath, "channels", _fileSystem.GetValidFilename(serviceName), _fileSystem.GetValidFilename(channelInfo.Name)); var fileInfo = new DirectoryInfo(path); @@ -249,7 +242,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv isNew = true; } - var id = GetInternalChannelId(channelInfo.ServiceName, channelInfo.Id); + var id = GetInternalChannelId(serviceName, channelInfo.Id); var item = _itemRepo.RetrieveItem(id) as Channel; @@ -264,7 +257,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv Path = path, ChannelId = channelInfo.Id, ChannelNumber = channelInfo.Number, - ServiceName = channelInfo.ServiceName + ServiceName = serviceName }; isNew = true; @@ -278,7 +271,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv return item; } - public QueryResult GetPrograms(ProgramQuery query) + public async Task> GetPrograms(ProgramQuery query, CancellationToken cancellationToken) { IEnumerable programs = _programs .OrderBy(i => i.StartDate) @@ -298,35 +291,34 @@ namespace MediaBrowser.Server.Implementations.LiveTv var returnArray = programs.ToArray(); - return new QueryResult + var recordings = await GetRecordings(new RecordingQuery { - Items = returnArray, - TotalRecordCount = returnArray.Length - }; - } - internal async Task RefreshChannels(IProgress progress, CancellationToken cancellationToken) - { - await _updateSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - await RefreshChannelsInternal(progress, cancellationToken).ConfigureAwait(false); - } - finally + }, cancellationToken).ConfigureAwait(false); + + foreach (var program in returnArray) { - _updateSemaphore.Release(); + var recording = recordings.Items + .FirstOrDefault(i => string.Equals(i.ProgramId, program.Id)); + + program.RecordingId = recording == null ? null : recording.Id; + program.RecordingStatus = recording == null ? (RecordingStatus?)null : recording.Status; } - await RefreshRecordings(new Progress(), cancellationToken).ConfigureAwait(false); + return new QueryResult + { + Items = returnArray, + TotalRecordCount = returnArray.Length + }; } - private async Task RefreshChannelsInternal(IProgress progress, CancellationToken cancellationToken) + internal async Task RefreshChannels(IProgress progress, CancellationToken cancellationToken) { // Avoid implicitly captured closure var currentCancellationToken = cancellationToken; - var channelTasks = _services.Select(i => i.GetChannelsAsync(currentCancellationToken)); + var channelTasks = _services.Select(i => GetChannels(i, currentCancellationToken)); progress.Report(10); @@ -343,11 +335,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv { try { - var item = await GetChannel(channelInfo, cancellationToken).ConfigureAwait(false); + var item = await GetChannel(channelInfo.Item2, channelInfo.Item1, cancellationToken).ConfigureAwait(false); - var service = GetService(channelInfo); + var service = _services.First(i => string.Equals(channelInfo.Item1, i.Name, StringComparison.OrdinalIgnoreCase)); - var channelPrograms = await service.GetProgramsAsync(channelInfo.Id, cancellationToken).ConfigureAwait(false); + var channelPrograms = await service.GetProgramsAsync(channelInfo.Item2.Id, cancellationToken).ConfigureAwait(false); programs.AddRange(channelPrograms.Select(program => GetProgramInfoDto(program, item))); @@ -359,7 +351,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv } catch (Exception ex) { - _logger.ErrorException("Error getting channel information for {0}", ex, channelInfo.Name); + _logger.ErrorException("Error getting channel information for {0}", ex, channelInfo.Item2.Name); } numComplete++; @@ -373,32 +365,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv _channels = list; } - internal async Task RefreshRecordings(IProgress progress, CancellationToken cancellationToken) - { - await _updateSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - await RefreshRecordingsInternal(progress, cancellationToken).ConfigureAwait(false); - } - finally - { - _updateSemaphore.Release(); - } - } - - private async Task RefreshRecordingsInternal(IProgress progress, CancellationToken cancellationToken) + private async Task>> GetChannels(ILiveTvService service, CancellationToken cancellationToken) { - var list = new List(); - - foreach (var service in _services) - { - var recordings = await GetRecordings(service, cancellationToken).ConfigureAwait(false); - - list.AddRange(recordings); - } + var channels = await service.GetChannelsAsync(cancellationToken).ConfigureAwait(false); - _recordings = list; + return channels.Select(i => new Tuple(service.Name, i)); } private async Task> GetRecordings(ILiveTvService service, CancellationToken cancellationToken) @@ -419,7 +390,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv Description = info.Description, EndDate = info.EndDate, Name = info.Name, - IsRecurring = info.IsRecurring, StartDate = info.StartDate, Id = id, ExternalId = info.Id, @@ -427,17 +397,27 @@ namespace MediaBrowser.Server.Implementations.LiveTv Status = info.Status }; - if (!string.IsNullOrEmpty(info.ProgramId)) - { - dto.ProgramId = GetInternalProgramIdId(service.Name, info.ProgramId).ToString("N"); - } - return dto; } - public QueryResult GetRecordings() + public async Task> GetRecordings(RecordingQuery query, CancellationToken cancellationToken) { - var returnArray = _recordings.ToArray(); + var list = new List(); + + foreach (var service in GetServices(query.ServiceName, query.ChannelId)) + { + var recordings = await GetRecordings(service, cancellationToken).ConfigureAwait(false); + + list.AddRange(recordings); + } + + if (!string.IsNullOrEmpty(query.ChannelId)) + { + list = list.Where(i => string.Equals(i.ChannelId, query.ChannelId)) + .ToList(); + } + + var returnArray = list.ToArray(); return new QueryResult { @@ -445,5 +425,31 @@ namespace MediaBrowser.Server.Implementations.LiveTv TotalRecordCount = returnArray.Length }; } + + private IEnumerable GetServices(string serviceName, string channelId) + { + IEnumerable services = _services; + + if (string.IsNullOrEmpty(serviceName) && !string.IsNullOrEmpty(channelId)) + { + var channelIdGuid = new Guid(channelId); + + serviceName = _channels.Where(i => i.Id == channelIdGuid) + .Select(i => i.ServiceName) + .FirstOrDefault(); + } + + if (!string.IsNullOrEmpty(serviceName)) + { + services = services.Where(i => string.Equals(i.Name, serviceName, StringComparison.OrdinalIgnoreCase)); + } + + return services; + } + + public Task ScheduleRecording(string programId) + { + throw new NotImplementedException(); + } } } diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index d5a264689e..2b2c47db80 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.250 + 3.0.251 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index f5333c078e..a7a3150e68 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.250 + 3.0.251 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 6b3448f0e2..da0705c77e 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.250 + 3.0.251 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - + From 9f5fbfa8551e22287080fa28af1177279c72db21 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 26 Nov 2013 16:39:01 -0500 Subject: [PATCH 31/67] updated nuget --- Nuget/MediaBrowser.Common.Internal.nuspec | 4 ++-- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 2b2c47db80..51bdf43d66 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.251 + 3.0.252 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index a7a3150e68..6ccfca542e 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.251 + 3.0.252 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index da0705c77e..de9b90a461 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.251 + 3.0.252 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - + From 318b210da9a60bfa508fbf743a6f7e4d1b3ecf73 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 26 Nov 2013 17:17:54 -0800 Subject: [PATCH 32/67] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 433eaf2b87..105f46e950 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ It features a REST-based api with built-in documention to facilitate client deve We have several client apps released and in production: -- Android +- [Android](https://play.google.com/store/apps/details?id=com.mb.android "Android") - Html5 - [iOS](https://itunes.apple.com/us/app/media-browser-for-ios/id705058087 "iOS") - [Media Portal](http://www.team-mediaportal.com/ "Media Portal") From 64818ebd223880d1ef7d7173b10968077d2378b0 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 26 Nov 2013 21:38:11 -0500 Subject: [PATCH 33/67] fix directory watchers not picking up changes --- .../Library/LibraryStructureService.cs | 33 ++++++++++++++++--- MediaBrowser.Api/TvShowsService.cs | 12 +------ .../LiveTv/ILiveTvManager.cs | 4 +-- MediaBrowser.Model/LiveTv/ProgramInfoDto.cs | 12 +++++++ .../TV/EpisodeProviderFromXml.cs | 1 - .../IO/DirectoryWatchers.cs | 2 +- .../Library/LibraryManager.cs | 14 ++++++++ .../LiveTv/RefreshChannelsScheduledTask.cs | 2 +- .../ApplicationHost.cs | 2 -- 9 files changed, 59 insertions(+), 23 deletions(-) diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs index f3306bb63c..198bec1a0d 100644 --- a/MediaBrowser.Api/Library/LibraryStructureService.cs +++ b/MediaBrowser.Api/Library/LibraryStructureService.cs @@ -286,7 +286,12 @@ namespace MediaBrowser.Api.Library } finally { - _directoryWatchers.Start(); + // No need to start if scanning the library because it will handle it + if (!request.RefreshLibrary) + { + _directoryWatchers.Start(); + } + _directoryWatchers.RemoveTempIgnore(virtualFolderPath); } @@ -353,7 +358,12 @@ namespace MediaBrowser.Api.Library } finally { - _directoryWatchers.Start(); + // No need to start if scanning the library because it will handle it + if (!request.RefreshLibrary) + { + _directoryWatchers.Start(); + } + _directoryWatchers.RemoveTempIgnore(currentPath); _directoryWatchers.RemoveTempIgnore(newPath); } @@ -404,7 +414,12 @@ namespace MediaBrowser.Api.Library } finally { - _directoryWatchers.Start(); + // No need to start if scanning the library because it will handle it + if (!request.RefreshLibrary) + { + _directoryWatchers.Start(); + } + _directoryWatchers.RemoveTempIgnore(path); } @@ -442,7 +457,11 @@ namespace MediaBrowser.Api.Library } finally { - _directoryWatchers.Start(); + // No need to start if scanning the library because it will handle it + if (!request.RefreshLibrary) + { + _directoryWatchers.Start(); + } } if (request.RefreshLibrary) @@ -479,7 +498,11 @@ namespace MediaBrowser.Api.Library } finally { - _directoryWatchers.Start(); + // No need to start if scanning the library because it will handle it + if (!request.RefreshLibrary) + { + _directoryWatchers.Start(); + } } if (request.RefreshLibrary) diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index 1774f1a8ed..b36005c1aa 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -372,22 +372,12 @@ namespace MediaBrowser.Api return episodes.Where(i => (i.PhysicalSeasonNumber ?? -1) == seasonNumber); } - var episodeList = episodes.ToList(); - - // We can only enforce the air date requirement if the episodes have air dates - var enforceAirDate = episodeList.Any(i => i.PremiereDate.HasValue); - - return episodeList.Where(i => + return episodes.Where(i => { var episode = i; if (episode != null) { - if (enforceAirDate && !episode.PremiereDate.HasValue) - { - return false; - } - var currentSeasonNumber = episode.AiredSeasonNumber; return currentSeasonNumber.HasValue && currentSeasonNumber.Value == seasonNumber; diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index a04072d28d..91634b4bfb 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -1,7 +1,7 @@ -using System.Threading; -using MediaBrowser.Model.LiveTv; +using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Querying; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Controller.LiveTv diff --git a/MediaBrowser.Model/LiveTv/ProgramInfoDto.cs b/MediaBrowser.Model/LiveTv/ProgramInfoDto.cs index 2cafd288df..26cfd3cf00 100644 --- a/MediaBrowser.Model/LiveTv/ProgramInfoDto.cs +++ b/MediaBrowser.Model/LiveTv/ProgramInfoDto.cs @@ -100,6 +100,18 @@ namespace MediaBrowser.Model.LiveTv /// /// The recording status. public RecordingStatus? RecordingStatus { get; set; } + + /// + /// Gets or sets the timer identifier. + /// + /// The timer identifier. + public string TimerId { get; set; } + + /// + /// Gets or sets the timer status. + /// + /// The timer status. + public RecordingStatus? TimerStatus { get; set; } public ProgramInfoDto() { diff --git a/MediaBrowser.Providers/TV/EpisodeProviderFromXml.cs b/MediaBrowser.Providers/TV/EpisodeProviderFromXml.cs index b6fdaaa831..7ddf421b9b 100644 --- a/MediaBrowser.Providers/TV/EpisodeProviderFromXml.cs +++ b/MediaBrowser.Providers/TV/EpisodeProviderFromXml.cs @@ -2,7 +2,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; diff --git a/MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs b/MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs index b24cc20635..870a14bd80 100644 --- a/MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs +++ b/MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs @@ -69,7 +69,7 @@ namespace MediaBrowser.Server.Implementations.IO // This is an arbitraty amount of time, but delay it because file system writes often trigger events after RemoveTempIgnore has been called. // Seeing long delays in some situations, especially over the network. // Seeing delays up to 40 seconds, but not going to ignore changes for that long. - await Task.Delay(20000).ConfigureAwait(false); + await Task.Delay(1500).ConfigureAwait(false); string val; _tempIgnoredPaths.TryRemove(path, out val); diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 58a2fcd7ed..74c4f8b2a8 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -905,6 +905,20 @@ namespace MediaBrowser.Server.Implementations.Library /// The cancellation token. /// Task. public async Task ValidateMediaLibraryInternal(IProgress progress, CancellationToken cancellationToken) + { + _directoryWatchersFactory().Stop(); + + try + { + await PerformLibraryValidation(progress, cancellationToken).ConfigureAwait(false); + } + finally + { + _directoryWatchersFactory().Start(); + } + } + + private async Task PerformLibraryValidation(IProgress progress, CancellationToken cancellationToken) { _logger.Info("Validating media library"); diff --git a/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs b/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs index c3803d9bbc..3e7feeff79 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Server.Implementations.LiveTv { - class RefreshChannelsScheduledTask : IScheduledTask + class RefreshChannelsScheduledTask { private readonly ILiveTvManager _liveTvManager; diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index c53277d778..fcd7d299cf 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -199,8 +199,6 @@ namespace MediaBrowser.ServerApplication { await base.RunStartupTasks().ConfigureAwait(false); - DirectoryWatchers.Start(); - Logger.Info("Core startup complete"); Parallel.ForEach(GetExports(), entryPoint => From 6e3b8420baeca7fce91979a78ccff48457ac82e6 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 26 Nov 2013 21:41:29 -0500 Subject: [PATCH 34/67] removed test code --- .../LiveTv/RefreshChannelsScheduledTask.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs b/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs index 3e7feeff79..c3803d9bbc 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Server.Implementations.LiveTv { - class RefreshChannelsScheduledTask + class RefreshChannelsScheduledTask : IScheduledTask { private readonly ILiveTvManager _liveTvManager; From 1e9ffb83cf060bf2f755cd5a62782209eaaa6a4b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 27 Nov 2013 14:04:19 -0500 Subject: [PATCH 35/67] added live tv timers page --- MediaBrowser.Api/Images/ImageService.cs | 55 +++++++++++++ MediaBrowser.Api/LiveTv/LiveTvService.cs | 23 ++++++ .../LiveTv/ILiveTvManager.cs | 8 ++ .../MediaBrowser.Model.Portable.csproj | 3 + .../MediaBrowser.Model.net35.csproj | 3 + MediaBrowser.Model/LiveTv/RecordingQuery.cs | 15 ++++ MediaBrowser.Model/LiveTv/TimerInfoDto.cs | 72 +++++++++++++++++ MediaBrowser.Model/MediaBrowser.Model.csproj | 1 + .../LiveTv/LiveTvManager.cs | 62 ++++++++++++++- .../Sorting/AiredEpisodeOrderComparer.cs | 19 ++--- .../MediaBrowser.ServerApplication.csproj | 6 +- .../packages.config | 2 +- .../Api/DashboardService.cs | 3 +- MediaBrowser.WebDashboard/ApiClient.js | 79 +++++++++++++++++-- .../MediaBrowser.WebDashboard.csproj | 72 ++++++++--------- MediaBrowser.WebDashboard/packages.config | 2 +- 16 files changed, 368 insertions(+), 57 deletions(-) create mode 100644 MediaBrowser.Model/LiveTv/TimerInfoDto.cs diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 9112518b86..27881d12ba 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -269,6 +269,19 @@ namespace MediaBrowser.Api.Images public Guid Id { get; set; } } + [Route("/LiveTv/Channels/{Id}/Images/{Type}", "DELETE")] + [Route("/LiveTv/Channels/{Id}/Images/{Type}/{Index}", "DELETE")] + [Api(Description = "Deletes an item image")] + public class DeleteChannelImage : DeleteImageRequest, IReturnVoid + { + /// + /// Gets or sets the id. + /// + /// The id. + [ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] + public string Id { get; set; } + } + /// /// Class PostUserImage /// @@ -344,6 +357,25 @@ namespace MediaBrowser.Api.Images public Stream RequestStream { get; set; } } + [Route("/LiveTv/Channels/{Id}/Images/{Type}", "POST")] + [Route("/LiveTv/Channels/{Id}/Images/{Type}/{Index}", "POST")] + [Api(Description = "Posts an item image")] + public class PostChannelImage : DeleteImageRequest, IRequiresRequestStream, IReturnVoid + { + /// + /// Gets or sets the id. + /// + /// The id. + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string Id { get; set; } + + /// + /// The raw Http Request Input Stream + /// + /// The request stream. + public Stream RequestStream { get; set; } + } + /// /// Class ImageService /// @@ -622,6 +654,20 @@ namespace MediaBrowser.Api.Images Task.WaitAll(task); } + public void Post(PostChannelImage request) + { + var pathInfo = PathInfo.Parse(RequestContext.PathInfo); + var id = pathInfo.GetArgumentValue(2); + + request.Type = (ImageType)Enum.Parse(typeof(ImageType), pathInfo.GetArgumentValue(4), true); + + var item = _liveTv.GetChannel(id); + + var task = PostImage(item, request.RequestStream, request.Type, RequestContext.ContentType); + + Task.WaitAll(task); + } + /// /// Deletes the specified request. /// @@ -648,6 +694,15 @@ namespace MediaBrowser.Api.Images Task.WaitAll(task); } + public void Delete(DeleteChannelImage request) + { + var item = _liveTv.GetChannel(request.Id); + + var task = item.DeleteImage(request.Type, request.Index); + + Task.WaitAll(task); + } + /// /// Deletes the specified request. /// diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index db50f463d4..2961c920f8 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -57,6 +57,17 @@ namespace MediaBrowser.Api.LiveTv public string ChannelId { get; set; } } + [Route("/LiveTv/Timers", "GET")] + [Api(Description = "Gets live tv timers")] + public class GetTimers : IReturn> + { + [ApiMember(Name = "ServiceName", Description = "Optional filter by service.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ServiceName { get; set; } + + [ApiMember(Name = "ChannelId", Description = "Optional filter by channel id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ChannelId { get; set; } + } + [Route("/LiveTv/Programs", "GET")] [Api(Description = "Gets available live tv epgs..")] public class GetPrograms : IReturn> @@ -153,5 +164,17 @@ namespace MediaBrowser.Api.LiveTv return ToOptimizedResult(result); } + + public object Get(GetTimers request) + { + var result = _liveTvManager.GetTimers(new TimerQuery + { + ChannelId = request.ChannelId, + ServiceName = request.ServiceName + + }, CancellationToken.None).Result; + + return ToOptimizedResult(result); + } } } \ No newline at end of file diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 91634b4bfb..7938c38ec9 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -45,6 +45,14 @@ namespace MediaBrowser.Controller.LiveTv /// QueryResult{RecordingInfoDto}. Task> GetRecordings(RecordingQuery query, CancellationToken cancellationToken); + /// + /// Gets the timers. + /// + /// The query. + /// The cancellation token. + /// Task{QueryResult{TimerInfoDto}}. + Task> GetTimers(TimerQuery query, CancellationToken cancellationToken); + /// /// Gets the channel. /// diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index 6962cb470b..67888aa46d 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -251,6 +251,9 @@ LiveTv\RecordingStatus.cs + + LiveTv\TimerInfoDto.cs + Logging\ILogger.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 0bf2684bfd..cfe4a5462f 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -238,6 +238,9 @@ LiveTv\RecordingStatus.cs + + LiveTv\TimerInfoDto.cs + Logging\ILogger.cs diff --git a/MediaBrowser.Model/LiveTv/RecordingQuery.cs b/MediaBrowser.Model/LiveTv/RecordingQuery.cs index 8c83b0fcbd..0820c7785e 100644 --- a/MediaBrowser.Model/LiveTv/RecordingQuery.cs +++ b/MediaBrowser.Model/LiveTv/RecordingQuery.cs @@ -17,4 +17,19 @@ /// The name of the service. public string ServiceName { get; set; } } + + public class TimerQuery + { + /// + /// Gets or sets the channel identifier. + /// + /// The channel identifier. + public string ChannelId { get; set; } + + /// + /// Gets or sets the name of the service. + /// + /// The name of the service. + public string ServiceName { get; set; } + } } diff --git a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs new file mode 100644 index 0000000000..6a8339031a --- /dev/null +++ b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Model.LiveTv +{ + public class TimerInfoDto + { + /// + /// Id of the recording. + /// + public string Id { get; set; } + + /// + /// Gets or sets the external identifier. + /// + /// The external identifier. + public string ExternalId { get; set; } + + /// + /// ChannelId of the recording. + /// + public string ChannelId { get; set; } + + /// + /// ChannelName of the recording. + /// + public string ChannelName { get; set; } + + /// + /// Name of the recording. + /// + public string Name { get; set; } + + /// + /// Description of the recording. + /// + public string Description { get; set; } + + /// + /// The start date of the recording, in UTC. + /// + public DateTime StartDate { get; set; } + + /// + /// The end date of the recording, in UTC. + /// + public DateTime EndDate { get; set; } + + /// + /// Gets or sets the status. + /// + /// The status. + public RecordingStatus Status { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is recurring. + /// + /// true if this instance is recurring; otherwise, false. + public bool IsRecurring { get; set; } + + /// + /// Gets or sets the recurring days. + /// + /// The recurring days. + public List RecurringDays { get; set; } + + public TimerInfoDto() + { + RecurringDays = new List(); + } + } +} diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 1cbdc60efa..103e583aed 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -66,6 +66,7 @@ + diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 12241876a1..00ac83f15c 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -417,7 +417,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv .ToList(); } - var returnArray = list.ToArray(); + var returnArray = list.OrderByDescending(i => i.StartDate) + .ToArray(); return new QueryResult { @@ -451,5 +452,64 @@ namespace MediaBrowser.Server.Implementations.LiveTv { throw new NotImplementedException(); } + + public async Task> GetTimers(TimerQuery query, CancellationToken cancellationToken) + { + var list = new List(); + + foreach (var service in GetServices(query.ServiceName, query.ChannelId)) + { + var timers = await GetTimers(service, cancellationToken).ConfigureAwait(false); + + list.AddRange(timers); + } + + if (!string.IsNullOrEmpty(query.ChannelId)) + { + list = list.Where(i => string.Equals(i.ChannelId, query.ChannelId)) + .ToList(); + } + + var returnArray = list.OrderByDescending(i => i.StartDate) + .ToArray(); + + return new QueryResult + { + Items = returnArray, + TotalRecordCount = returnArray.Length + }; + } + + private async Task> GetTimers(ILiveTvService service, CancellationToken cancellationToken) + { + var timers = await service.GetTimersAsync(cancellationToken).ConfigureAwait(false); + + return timers.Select(i => GetTimerInfoDto(i, service)); + } + + private TimerInfoDto GetTimerInfoDto(TimerInfo info, ILiveTvService service) + { + var id = service.Name + info.ChannelId + info.Id; + id = id.GetMD5().ToString("N"); + + var dto = new TimerInfoDto + { + ChannelName = info.ChannelName, + Description = info.Description, + EndDate = info.EndDate, + Name = info.Name, + StartDate = info.StartDate, + Id = id, + ExternalId = info.Id, + ChannelId = GetInternalChannelId(service.Name, info.ChannelId).ToString("N"), + Status = info.Status, + IsRecurring = info.IsRecurring, + RecurringDays = info.RecurringDays + }; + + return dto; + } + + } } diff --git a/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs b/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs index bdc343dea6..76971342a0 100644 --- a/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs +++ b/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Server.Implementations.Sorting if (val != 0) { - return val; + //return val; } } @@ -49,8 +49,8 @@ namespace MediaBrowser.Server.Implementations.Sorting private int Compare(Episode x, Episode y) { - var isXSpecial = (x.ParentIndexNumber ?? -1) == 0; - var isYSpecial = (y.ParentIndexNumber ?? -1) == 0; + var isXSpecial = (x.PhysicalSeasonNumber ?? -1) == 0; + var isYSpecial = (y.PhysicalSeasonNumber ?? -1) == 0; if (isXSpecial && isYSpecial) { @@ -67,12 +67,12 @@ namespace MediaBrowser.Server.Implementations.Sorting return CompareEpisodeToSpecial(x, y); } - return CompareEpisodeToSpecial(x, y) * -1; + return CompareEpisodeToSpecial(y, x) * -1; } private int CompareEpisodeToSpecial(Episode x, Episode y) { - var xSeason = x.ParentIndexNumber ?? -1; + var xSeason = x.PhysicalSeasonNumber ?? -1; var ySeason = y.AirsAfterSeasonNumber ?? y.AirsBeforeSeasonNumber ?? -1; if (xSeason != ySeason) @@ -85,8 +85,9 @@ namespace MediaBrowser.Server.Implementations.Sorting // Compare episode number // Add 1 to to non-specials to account for AirsBeforeEpisodeNumber - var xEpisode = (x.IndexNumber ?? 0) * 1000 + 1; - var yEpisode = (y.AirsBeforeEpisodeNumber ?? 0) * 1000; + var xEpisode = x.IndexNumber ?? -1; + xEpisode++; + var yEpisode = y.AirsBeforeEpisodeNumber ?? 10000; return xEpisode.CompareTo(yEpisode); } @@ -119,8 +120,8 @@ namespace MediaBrowser.Server.Implementations.Sorting private int CompareEpisodes(Episode x, Episode y) { - var xValue = ((x.ParentIndexNumber ?? -1) * 1000) + (x.IndexNumber ?? -1); - var yValue = ((y.ParentIndexNumber ?? -1) * 1000) + (y.IndexNumber ?? -1); + var xValue = ((x.PhysicalSeasonNumber ?? -1) * 1000) + (x.IndexNumber ?? -1); + var yValue = ((y.PhysicalSeasonNumber ?? -1) * 1000) + (y.IndexNumber ?? -1); return xValue.CompareTo(yValue); } diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index f24283e70d..cfacffe087 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -119,9 +119,9 @@ ..\packages\Hardcodet.Wpf.TaskbarNotification.1.0.4.0\lib\net40\Hardcodet.Wpf.TaskbarNotification.dll - + False - ..\packages\MediaBrowser.IsoMounting.3.0.61\lib\net45\MediaBrowser.IsoMounter.dll + ..\packages\MediaBrowser.IsoMounting.3.0.65\lib\net45\MediaBrowser.IsoMounter.dll False @@ -129,7 +129,7 @@ False - ..\packages\MediaBrowser.IsoMounting.3.0.61\lib\net45\pfmclrapi.dll + ..\packages\MediaBrowser.IsoMounting.3.0.65\lib\net45\pfmclrapi.dll False diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config index 0893a1b38a..e01ca1f672 100644 --- a/MediaBrowser.ServerApplication/packages.config +++ b/MediaBrowser.ServerApplication/packages.config @@ -1,7 +1,7 @@  - + diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 2b74eebb0c..69f05631f3 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -430,7 +430,7 @@ namespace MediaBrowser.WebDashboard.Api "http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js", "http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js", "scripts/all.js" + versionString, - "thirdparty/jstree1.0fix2/jquery.jstree.js" + "thirdparty/jstree1.0fix3/jquery.jstree.js" }; var tags = files.Select(s => string.Format("", s)).ToArray(); @@ -483,6 +483,7 @@ namespace MediaBrowser.WebDashboard.Api "livetvchannels.js", "livetvguide.js", "livetvrecordings.js", + "livetvtimers.js", "loginpage.js", "logpage.js", "medialibrarypage.js", diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js index e63eb4d2b1..69f3f020cf 100644 --- a/MediaBrowser.WebDashboard/ApiClient.js +++ b/MediaBrowser.WebDashboard/ApiClient.js @@ -380,7 +380,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi self.getLiveTvServices = function (options) { - var url = self.getUrl("/LiveTv/Services", options || {}); + var url = self.getUrl("LiveTv/Services", options || {}); return self.ajax({ type: "GET", @@ -395,7 +395,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi throw new Error("null id"); } - var url = self.getUrl("/LiveTv/Channels/" + id); + var url = self.getUrl("LiveTv/Channels/" + id); return self.ajax({ type: "GET", @@ -406,7 +406,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi self.getLiveTvChannels = function (options) { - var url = self.getUrl("/LiveTv/Channels", options || {}); + var url = self.getUrl("LiveTv/Channels", options || {}); return self.ajax({ type: "GET", @@ -417,7 +417,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi self.getLiveTvPrograms = function (options) { - var url = self.getUrl("/LiveTv/Programs", options || {}); + var url = self.getUrl("LiveTv/Programs", options || {}); return self.ajax({ type: "GET", @@ -428,7 +428,76 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi self.getLiveTvRecordings = function (options) { - var url = self.getUrl("/LiveTv/Recordings", options || {}); + var url = self.getUrl("LiveTv/Recordings", options || {}); + + return self.ajax({ + type: "GET", + url: url, + dataType: "json" + }); + }; + + self.getLiveTvRecording = function (id) { + + if (!id) { + throw new Error("null id"); + } + + var url = self.getUrl("LiveTv/Recordings/" + id); + + return self.ajax({ + type: "GET", + url: url, + dataType: "json" + }); + }; + + self.deleteLiveTvRecording = function (id) { + + if (!id) { + throw new Error("null id"); + } + + var url = self.getUrl("LiveTv/Recordings/" + id); + + return self.ajax({ + type: "DELETE", + url: url + }); + }; + + self.cancelLiveTvTimer = function (id) { + + if (!id) { + throw new Error("null id"); + } + + var url = self.getUrl("LiveTv/Timers/" + id); + + return self.ajax({ + type: "DELETE", + url: url + }); + }; + + self.getLiveTvTimers = function (options) { + + var url = self.getUrl("LiveTv/Timers", options || {}); + + return self.ajax({ + type: "GET", + url: url, + dataType: "json" + }); + }; + + self.getLiveTvTimer = function (id) { + + if (!id) { + throw new Error("null id"); + } + + var url = self.getUrl("LiveTv/Timers/" + id); return self.ajax({ type: "GET", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 54e70cb9ab..5585e0db5e 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -80,9 +80,18 @@ + + PreserveNewest + + + PreserveNewest + PreserveNewest + + PreserveNewest + PreserveNewest @@ -281,18 +290,6 @@ PreserveNewest - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - PreserveNewest @@ -350,6 +347,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -595,76 +595,76 @@ PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config index ab57a48b15..35511052f4 100644 --- a/MediaBrowser.WebDashboard/packages.config +++ b/MediaBrowser.WebDashboard/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file From 5bf12c3bad26a8eab095d33f200486549b88a265 Mon Sep 17 00:00:00 2001 From: tikuf Date: Thu, 28 Nov 2013 16:51:38 +1100 Subject: [PATCH 36/67] Better Thumb extraction. Remove Black bars if we make them. Fix fsbs and ftab image extraction. --- .../MediaEncoder/MediaEncoder.cs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs index b8874fb548..2224c657ff 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs @@ -864,25 +864,33 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder throw new ArgumentNullException("outputPath"); } - var vf = "scale=iw*sar:ih, scale=600:-1"; - + var vf = "crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,scale=600:600/dar"; + // apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar then scale to width 600. This filter chain may have adverse effects on recorded tv thumbnails if ar changes during presentation ex. commercials @ diff ar if (threedFormat.HasValue) { switch (threedFormat.Value) { case Video3DFormat.HalfSideBySide: + vf = "crop=iw/2:ih:0:0,scale=(iw*2):ih,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,scale=600:600/dar"; + // hsbs crop width in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to 600. Work out the correct height based on the display aspect it will maintain the aspect where -1 in this case (3d) may not. + break; case Video3DFormat.FullSideBySide: - vf = "crop=iw/2:ih:0:0,scale=(iw*2):ih,scale=600:-1"; + vf = "crop=iw/2:ih:0:0,setdar=dar=a,,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,scale=600:600/dar"; + //fsbs crop width in half,set the display aspect,crop out any black bars we may have made the scale width to 600. break; case Video3DFormat.HalfTopAndBottom: + vf = "crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,scale=600:600/dar"; + //htab crop heigh in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to 600 + break; case Video3DFormat.FullTopAndBottom: - vf = "crop=iw:ih/2:0:0,scale=iw:(ih*2),scale=600:-1"; + vf = "crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,scale=600:600/dar"; + // ftab crop heigt in half, set the display aspect,crop out any black bars we may have made the scale width to 600 break; } } - var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -filter:v select=\"eq(pict_type\\,I)\" -vf \"{2}\" -f image2 \"{1}\"", inputPath, outputPath, vf) : - string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, outputPath, vf); + var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"thumbnail,{2}\" -f image2 \"{1}\"", inputPath, outputPath, vf) : + string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, outputPath, vf); //use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back jic var probeSize = GetProbeSizeArgument(type); From 4892fb4e95f982527769620595e924c364204310 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 28 Nov 2013 13:27:29 -0500 Subject: [PATCH 37/67] add specialized get seasons method --- .../BaseProgressiveStreamingService.cs | 82 ++++++++--------- MediaBrowser.Api/TvShowsService.cs | 90 ++++++++++++++++++- MediaBrowser.Controller/Entities/TV/Season.cs | 10 ++- MediaBrowser.Model/ApiClient/IApiClient.cs | 7 ++ MediaBrowser.Model/Querying/EpisodeQuery.cs | 19 ++++ .../MediaBrowser.Providers.csproj | 1 + .../UserRootFolderNameProvider.cs | 42 +++++++++ MediaBrowser.WebDashboard/ApiClient.js | 11 +++ MediaBrowser.WebDashboard/packages.config | 2 +- 9 files changed, 218 insertions(+), 46 deletions(-) create mode 100644 MediaBrowser.Providers/UserRootFolderNameProvider.cs diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 0b7b48f47e..1f0853e08b 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -127,44 +127,44 @@ namespace MediaBrowser.Api.Playback.Progressive const string dlnaflags = ";DLNA.ORG_FLAGS=01500000000000000000000000000000"; - if (string.Equals(extension, ".mp3", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=MP3"; - } - else if (string.Equals(extension, ".aac", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=AAC_ISO"; - } - else if (string.Equals(extension, ".wma", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=WMABASE"; - } - else if (string.Equals(extension, ".avi", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=AVI"; - } - else if (string.Equals(extension, ".mp4", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=MPEG4_P2_SP_AAC"; - } - else if (string.Equals(extension, ".mpeg", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL"; - } - else if (string.Equals(extension, ".wmv", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE"; - } - else if (string.Equals(extension, ".asf", StringComparison.OrdinalIgnoreCase)) - { - // ?? - contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE"; - } - else if (string.Equals(extension, ".mkv", StringComparison.OrdinalIgnoreCase)) - { - // ?? - contentFeatures = ""; - } + //if (string.Equals(extension, ".mp3", StringComparison.OrdinalIgnoreCase)) + //{ + // contentFeatures = "DLNA.ORG_PN=MP3"; + //} + //else if (string.Equals(extension, ".aac", StringComparison.OrdinalIgnoreCase)) + //{ + // contentFeatures = "DLNA.ORG_PN=AAC_ISO"; + //} + //else if (string.Equals(extension, ".wma", StringComparison.OrdinalIgnoreCase)) + //{ + // contentFeatures = "DLNA.ORG_PN=WMABASE"; + //} + //else if (string.Equals(extension, ".avi", StringComparison.OrdinalIgnoreCase)) + //{ + // contentFeatures = "DLNA.ORG_PN=AVI"; + //} + //else if (string.Equals(extension, ".mp4", StringComparison.OrdinalIgnoreCase)) + //{ + // contentFeatures = "DLNA.ORG_PN=MPEG4_P2_SP_AAC"; + //} + //else if (string.Equals(extension, ".mpeg", StringComparison.OrdinalIgnoreCase)) + //{ + // contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL"; + //} + //else if (string.Equals(extension, ".wmv", StringComparison.OrdinalIgnoreCase)) + //{ + // contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE"; + //} + //else if (string.Equals(extension, ".asf", StringComparison.OrdinalIgnoreCase)) + //{ + // // ?? + // contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE"; + //} + //else if (string.Equals(extension, ".mkv", StringComparison.OrdinalIgnoreCase)) + //{ + // // ?? + // contentFeatures = ""; + //} if (!string.IsNullOrEmpty(contentFeatures)) { @@ -206,10 +206,10 @@ namespace MediaBrowser.Api.Playback.Progressive var outputPath = GetOutputFilePath(state); var outputPathExists = File.Exists(outputPath); - //var isStatic = request.Static || - // (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive)); + var isStatic = request.Static || + (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive)); - //AddDlnaHeaders(state, responseHeaders, isStatic); + AddDlnaHeaders(state, responseHeaders, isStatic); if (request.Static) { diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index b36005c1aa..95bde2d283 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -58,7 +58,7 @@ namespace MediaBrowser.Api } [Route("/Shows/{Id}/Episodes", "GET")] - [Api(Description = "Finds tv shows similar to a given one.")] + [Api(Description = "Gets episodes for a tv season")] public class GetEpisodes : IReturn, IHasItemFields { /// @@ -85,6 +85,34 @@ namespace MediaBrowser.Api public string ExcludeLocationTypes { get; set; } } + [Route("/Shows/{Id}/Seasons", "GET")] + [Api(Description = "Gets seasons for a tv series")] + public class GetSeasons : IReturn, IHasItemFields + { + /// + /// Gets or sets the user id. + /// + /// The user id. + [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + public Guid UserId { get; set; } + + /// + /// Fields to return within the items, in addition to basic information + /// + /// The fields. + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, OverviewHtml, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + public string Fields { get; set; } + + [ApiMember(Name = "Id", Description = "The series id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + public Guid Id { get; set; } + + [ApiMember(Name = "ExcludeLocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + public string ExcludeLocationTypes { get; set; } + + [ApiMember(Name = "IsSpecialSeason", Description = "Optional. Filter by special season.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool? IsSpecialSeason { get; set; } + } + /// /// Class TvShowsService /// @@ -314,6 +342,64 @@ namespace MediaBrowser.Api return items; } + public object Get(GetSeasons request) + { + var user = _userManager.GetUserById(request.UserId); + + var series = _libraryManager.GetItemById(request.Id) as Series; + + var fields = request.GetItemFields().ToList(); + + var seasons = series.GetChildren(user, true) + .OfType(); + + var sortOrder = ItemSortBy.SortName; + + if (request.IsSpecialSeason.HasValue) + { + var val = request.IsSpecialSeason.Value; + + seasons = seasons.Where(i => i.IsSpecialSeason == val); + } + + var config = user.Configuration; + + if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes) + { + seasons = seasons.Where(i => !i.IsMissingOrVirtualUnaired); + } + else + { + if (!config.DisplayMissingEpisodes) + { + seasons = seasons.Where(i => !i.IsMissingSeason); + } + if (!config.DisplayUnairedEpisodes) + { + seasons = seasons.Where(i => !i.IsVirtualUnaired); + } + } + + // ExcludeLocationTypes + if (!string.IsNullOrEmpty(request.ExcludeLocationTypes)) + { + var vals = request.ExcludeLocationTypes.Split(','); + seasons = seasons.Where(f => !vals.Contains(f.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)); + } + + seasons = _libraryManager.Sort(seasons, user, new[] { sortOrder }, SortOrder.Ascending) + .Cast(); + + var returnItems = seasons.Select(i => _dtoService.GetBaseItemDto(i, fields, user)) + .ToArray(); + + return new ItemsResult + { + TotalRecordCount = returnItems.Length, + Items = returnItems + }; + } + public object Get(GetEpisodes request) { var user = _userManager.GetUserById(request.UserId); @@ -351,7 +437,7 @@ namespace MediaBrowser.Api var vals = request.ExcludeLocationTypes.Split(','); episodes = episodes.Where(f => !vals.Contains(f.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)); } - + episodes = _libraryManager.Sort(episodes, user, new[] { sortOrder }, SortOrder.Ascending) .Cast(); diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 97a09b7b55..218c9fa5b6 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -1,9 +1,9 @@ -using System.Linq; -using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Runtime.Serialization; namespace MediaBrowser.Controller.Entities.TV @@ -172,5 +172,11 @@ namespace MediaBrowser.Controller.Entities.TV { get { return LocationType == Model.Entities.LocationType.Virtual && Children.OfType().All(i => i.IsVirtualUnaired || i.IsMissingEpisode); } } + + [IgnoreDataMember] + public bool IsSpecialSeason + { + get { return (IndexNumber ?? -1) == 0; } + } } } diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index ad82966048..16b3543ebd 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -231,6 +231,13 @@ namespace MediaBrowser.Model.ApiClient /// The query. /// Task{ItemsResult}. Task GetEpisodesAsync(EpisodeQuery query); + + /// + /// Gets the seasons asynchronous. + /// + /// The query. + /// Task{ItemsResult}. + Task GetSeasonsAsync(SeasonQuery query); /// /// Queries for items diff --git a/MediaBrowser.Model/Querying/EpisodeQuery.cs b/MediaBrowser.Model/Querying/EpisodeQuery.cs index ab9b246c8c..406ac9844f 100644 --- a/MediaBrowser.Model/Querying/EpisodeQuery.cs +++ b/MediaBrowser.Model/Querying/EpisodeQuery.cs @@ -20,4 +20,23 @@ namespace MediaBrowser.Model.Querying ExcludeLocationTypes = new LocationType[] { }; } } + + public class SeasonQuery + { + public string UserId { get; set; } + + public string SeriesId { get; set; } + + public LocationType[] ExcludeLocationTypes { get; set; } + + public ItemFields[] Fields { get; set; } + + public bool? IsSpecialSeason { get; set; } + + public SeasonQuery() + { + Fields = new ItemFields[] { }; + ExcludeLocationTypes = new LocationType[] { }; + } + } } diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 83e0246c53..c7298b4622 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -135,6 +135,7 @@ + diff --git a/MediaBrowser.Providers/UserRootFolderNameProvider.cs b/MediaBrowser.Providers/UserRootFolderNameProvider.cs new file mode 100644 index 0000000000..5130202139 --- /dev/null +++ b/MediaBrowser.Providers/UserRootFolderNameProvider.cs @@ -0,0 +1,42 @@ +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Logging; +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Providers +{ + public class UserRootFolderNameProvider : BaseMetadataProvider + { + public UserRootFolderNameProvider(ILogManager logManager, IServerConfigurationManager configurationManager) + : base(logManager, configurationManager) + { + } + + public override bool Supports(BaseItem item) + { + return item is UserRootFolder; + } + + public override Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) + { + var parentName = Path.GetFileNameWithoutExtension(item.Path); + + if (string.Equals(parentName, "default", StringComparison.OrdinalIgnoreCase)) + { + item.Name = "Media Library"; + } + + SetLastRefreshed(item, DateTime.UtcNow); + return TrueTaskResult; + } + + public override MetadataProviderPriority Priority + { + get { return MetadataProviderPriority.First; } + } + } +} diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js index 69f3f020cf..36476511ec 100644 --- a/MediaBrowser.WebDashboard/ApiClient.js +++ b/MediaBrowser.WebDashboard/ApiClient.js @@ -575,6 +575,17 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi }); }; + self.getSeasons = function (itemId, options) { + + var url = self.getUrl("Shows/" + itemId + "/Seasons", options); + + return self.ajax({ + type: "GET", + url: url, + dataType: "json" + }); + }; + self.getSimilarMovies = function (itemId, options) { var url = self.getUrl("Movies/" + itemId + "/Similar", options); diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config index 35511052f4..25938a358a 100644 --- a/MediaBrowser.WebDashboard/packages.config +++ b/MediaBrowser.WebDashboard/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file From 235b838fbe262f3f41cd64c8506d067c9ef9253e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 29 Nov 2013 11:58:24 -0500 Subject: [PATCH 38/67] support deleting and canceling live tv recordings and timers --- MediaBrowser.Api/LiveTv/LiveTvService.cs | 31 +++++++++ .../ScheduledTasks/ScheduledTaskService.cs | 32 ++++++++-- .../ScheduledTasksWebSocketListener.cs | 6 +- MediaBrowser.Api/TvShowsService.cs | 64 ++++++++++++++----- .../BaseApplicationHost.cs | 2 +- .../HttpClientManager/HttpClientManager.cs | 3 +- .../Tasks/DeleteCacheFileTask.cs | 1 - .../ScheduledTasks/IScheduledTask.cs | 5 ++ .../ScheduledTasks/ScheduledTaskHelpers.cs | 12 +++- .../LiveTv/ILiveTvManager.cs | 14 ++++ .../LiveTv/ILiveTvService.cs | 8 --- .../LiveTv/RecordingInfo.cs | 6 ++ MediaBrowser.Controller/LiveTv/TimerInfo.cs | 18 ++---- MediaBrowser.Model/LiveTv/RecordingStatus.cs | 7 ++ MediaBrowser.Model/LiveTv/TimerInfoDto.cs | 24 +++---- MediaBrowser.Model/Querying/EpisodeQuery.cs | 13 ++-- MediaBrowser.Model/Tasks/TaskInfo.cs | 6 ++ .../LiveTv/LiveTvManager.cs | 57 ++++++++++++++++- .../LiveTv/RefreshChannelsScheduledTask.cs | 9 ++- .../Native/HttpClientFactory.cs | 7 +- MediaBrowser.WebDashboard/ApiClient.js | 20 +++++- .../MediaBrowser.WebDashboard.csproj | 4 +- MediaBrowser.WebDashboard/packages.config | 2 +- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- 26 files changed, 280 insertions(+), 81 deletions(-) diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 2961c920f8..4cd75afca5 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -1,4 +1,5 @@ using System.Threading; +using System.Threading.Tasks; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Querying; @@ -82,6 +83,22 @@ namespace MediaBrowser.Api.LiveTv public string UserId { get; set; } } + [Route("/LiveTv/Recordings/{Id}", "DELETE")] + [Api(Description = "Deletes a live tv recording")] + public class DeleteRecording : IReturnVoid + { + [ApiMember(Name = "Id", Description = "Recording Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + } + + [Route("/LiveTv/Timers/{Id}", "DELETE")] + [Api(Description = "Cancels a live tv timer")] + public class CancelTimer : IReturnVoid + { + [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + } + public class LiveTvService : BaseApiService { private readonly ILiveTvManager _liveTvManager; @@ -176,5 +193,19 @@ namespace MediaBrowser.Api.LiveTv return ToOptimizedResult(result); } + + public void Delete(DeleteRecording request) + { + var task = _liveTvManager.DeleteRecording(request.Id); + + Task.WaitAll(task); + } + + public void Delete(CancelTimer request) + { + var task = _liveTvManager.CancelTimer(request.Id); + + Task.WaitAll(task); + } } } \ No newline at end of file diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs index 2674529e51..d17a38e073 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs +++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs @@ -31,7 +31,8 @@ namespace MediaBrowser.Api.ScheduledTasks [Api(Description = "Gets scheduled tasks")] public class GetScheduledTasks : IReturn> { - + [ApiMember(Name = "IsHidden", Description = "Optional filter tasks that are hidden, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool? IsHidden { get; set; } } /// @@ -112,10 +113,33 @@ namespace MediaBrowser.Api.ScheduledTasks /// IEnumerable{TaskInfo}. public object Get(GetScheduledTasks request) { - var result = TaskManager.ScheduledTasks.OrderBy(i => i.Name) - .Select(ScheduledTaskHelpers.GetTaskInfo).ToList(); + IEnumerable result = TaskManager.ScheduledTasks + .OrderBy(i => i.Name); - return ToOptimizedResult(result); + if (request.IsHidden.HasValue) + { + var val = request.IsHidden.Value; + + result = result.Where(i => + { + var isHidden = false; + + var configurableTask = i.ScheduledTask as IConfigurableScheduledTask; + + if (configurableTask != null) + { + isHidden = configurableTask.IsHidden; + } + + return isHidden == val; + }); + } + + var infos = result + .Select(ScheduledTaskHelpers.GetTaskInfo) + .ToList(); + + return ToOptimizedResult(infos); } /// diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs index 20634301a3..c143635bfa 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs +++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs @@ -46,8 +46,10 @@ namespace MediaBrowser.Api.ScheduledTasks /// Task{IEnumerable{TaskInfo}}. protected override Task> GetDataToSend(object state) { - return Task.FromResult(TaskManager.ScheduledTasks.OrderBy(i => i.Name) - .Select(ScheduledTaskHelpers.GetTaskInfo)); + return Task.FromResult(TaskManager.ScheduledTasks + .OrderBy(i => i.Name) + .Select(ScheduledTaskHelpers.GetTaskInfo) + .Where(i => !i.IsHidden)); } } } diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index 95bde2d283..23b8efa7bd 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -81,8 +81,11 @@ namespace MediaBrowser.Api [ApiMember(Name = "Season", Description = "Optional filter by season number.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public int? Season { get; set; } - [ApiMember(Name = "ExcludeLocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string ExcludeLocationTypes { get; set; } + [ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool? IsMissing { get; set; } + + [ApiMember(Name = "IsVirtualUnaired", Description = "Optional filter by items that are virtual unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool? IsVirtualUnaired { get; set; } } [Route("/Shows/{Id}/Seasons", "GET")] @@ -106,11 +109,14 @@ namespace MediaBrowser.Api [ApiMember(Name = "Id", Description = "The series id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] public Guid Id { get; set; } - [ApiMember(Name = "ExcludeLocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string ExcludeLocationTypes { get; set; } - [ApiMember(Name = "IsSpecialSeason", Description = "Optional. Filter by special season.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? IsSpecialSeason { get; set; } + + [ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool? IsMissing { get; set; } + + [ApiMember(Name = "IsVirtualUnaired", Description = "Optional filter by items that are virtual unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool? IsVirtualUnaired { get; set; } } /// @@ -380,12 +386,7 @@ namespace MediaBrowser.Api } } - // ExcludeLocationTypes - if (!string.IsNullOrEmpty(request.ExcludeLocationTypes)) - { - var vals = request.ExcludeLocationTypes.Split(','); - seasons = seasons.Where(f => !vals.Contains(f.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)); - } + seasons = FilterVirtualSeasons(request, seasons); seasons = _libraryManager.Sort(seasons, user, new[] { sortOrder }, SortOrder.Ascending) .Cast(); @@ -400,6 +401,34 @@ namespace MediaBrowser.Api }; } + private IEnumerable FilterVirtualSeasons(GetSeasons request, IEnumerable items) + { + if (request.IsMissing.HasValue && request.IsVirtualUnaired.HasValue) + { + var isMissing = request.IsMissing.Value; + var isVirtualUnaired = request.IsVirtualUnaired.Value; + + if (!isMissing && !isVirtualUnaired) + { + return items.Where(i => !i.IsMissingOrVirtualUnaired); + } + } + + if (request.IsMissing.HasValue) + { + var val = request.IsMissing.Value; + items = items.Where(i => i.IsMissingSeason == val); + } + + if (request.IsVirtualUnaired.HasValue) + { + var val = request.IsVirtualUnaired.Value; + items = items.Where(i => i.IsVirtualUnaired == val); + } + + return items; + } + public object Get(GetEpisodes request) { var user = _userManager.GetUserById(request.UserId); @@ -431,11 +460,16 @@ namespace MediaBrowser.Api episodes = episodes.Where(i => !i.IsVirtualUnaired); } - // ExcludeLocationTypes - if (!string.IsNullOrEmpty(request.ExcludeLocationTypes)) + if (request.IsMissing.HasValue) + { + var val = request.IsMissing.Value; + episodes = episodes.Where(i => i.IsMissingEpisode == val); + } + + if (request.IsVirtualUnaired.HasValue) { - var vals = request.ExcludeLocationTypes.Split(','); - episodes = episodes.Where(f => !vals.Contains(f.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)); + var val = request.IsVirtualUnaired.Value; + episodes = episodes.Where(i => i.IsVirtualUnaired == val); } episodes = _libraryManager.Sort(episodes, user, new[] { sortOrder }, SortOrder.Ascending) diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs index ee22b7baa2..f1d8c94e55 100644 --- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs +++ b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs @@ -218,7 +218,7 @@ namespace MediaBrowser.Common.Implementations try { // Increase the max http request limit - ServicePointManager.DefaultConnectionLimit = Math.Max(48, ServicePointManager.DefaultConnectionLimit); + ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit); } catch (Exception ex) { diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs index 0d6ba5c1da..181c83fd3a 100644 --- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs +++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Configuration; +using System.Reflection; +using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Model.Logging; diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs index e04cadfc5a..6d886bc696 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs @@ -29,7 +29,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks /// /// Initializes a new instance of the class. /// - /// The app paths. public DeleteCacheFileTask(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem) { ApplicationPaths = appPaths; diff --git a/MediaBrowser.Common/ScheduledTasks/IScheduledTask.cs b/MediaBrowser.Common/ScheduledTasks/IScheduledTask.cs index 351e96c7d5..2ee4fb4b50 100644 --- a/MediaBrowser.Common/ScheduledTasks/IScheduledTask.cs +++ b/MediaBrowser.Common/ScheduledTasks/IScheduledTask.cs @@ -42,4 +42,9 @@ namespace MediaBrowser.Common.ScheduledTasks /// IEnumerable{BaseTaskTrigger}. IEnumerable GetDefaultTriggers(); } + + public interface IConfigurableScheduledTask + { + bool IsHidden { get; } + } } diff --git a/MediaBrowser.Common/ScheduledTasks/ScheduledTaskHelpers.cs b/MediaBrowser.Common/ScheduledTasks/ScheduledTaskHelpers.cs index f316509869..39148166bc 100644 --- a/MediaBrowser.Common/ScheduledTasks/ScheduledTaskHelpers.cs +++ b/MediaBrowser.Common/ScheduledTasks/ScheduledTaskHelpers.cs @@ -16,6 +16,15 @@ namespace MediaBrowser.Common.ScheduledTasks /// TaskInfo. public static TaskInfo GetTaskInfo(IScheduledTaskWorker task) { + var isHidden = false; + + var configurableTask = task.ScheduledTask as IConfigurableScheduledTask; + + if (configurableTask != null) + { + isHidden = configurableTask.IsHidden; + } + return new TaskInfo { Name = task.Name, @@ -25,7 +34,8 @@ namespace MediaBrowser.Common.ScheduledTasks LastExecutionResult = task.LastExecutionResult, Triggers = task.Triggers.Select(GetTriggerInfo).ToList(), Description = task.Description, - Category = task.Category + Category = task.Category, + IsHidden = isHidden }; } diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 7938c38ec9..d5b9cea270 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -24,6 +24,20 @@ namespace MediaBrowser.Controller.LiveTv /// Task. Task ScheduleRecording(string programId); + /// + /// Deletes the recording. + /// + /// The identifier. + /// Task. + Task DeleteRecording(string id); + + /// + /// Cancels the timer. + /// + /// The identifier. + /// Task. + Task CancelTimer(string id); + /// /// Adds the parts. /// diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs index e903ad5ec6..9bc032af35 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs @@ -47,14 +47,6 @@ namespace MediaBrowser.Controller.LiveTv /// Task. Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken); - /// - /// Updates the timer asynchronous. - /// - /// The information. - /// The cancellation token. - /// Task. - Task UpdateTimerAsync(TimerInfo info, CancellationToken cancellationToken); - /// /// Gets the channel image asynchronous. /// diff --git a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs index f0daac5f91..88d093f645 100644 --- a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs +++ b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs @@ -40,6 +40,12 @@ namespace MediaBrowser.Controller.LiveTv /// public DateTime EndDate { get; set; } + /// + /// Gets or sets the program identifier. + /// + /// The program identifier. + public string ProgramId { get; set; } + /// /// Gets or sets the status. /// diff --git a/MediaBrowser.Controller/LiveTv/TimerInfo.cs b/MediaBrowser.Controller/LiveTv/TimerInfo.cs index f0f936b526..7401dafec9 100644 --- a/MediaBrowser.Controller/LiveTv/TimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/TimerInfo.cs @@ -1,6 +1,5 @@ using MediaBrowser.Model.LiveTv; using System; -using System.Collections.Generic; namespace MediaBrowser.Controller.LiveTv { @@ -21,6 +20,12 @@ namespace MediaBrowser.Controller.LiveTv /// public string ChannelName { get; set; } + /// + /// Gets or sets the program identifier. + /// + /// The program identifier. + public string ProgramId { get; set; } + /// /// Name of the recording. /// @@ -52,16 +57,5 @@ namespace MediaBrowser.Controller.LiveTv /// /// true if this instance is recurring; otherwise, false. public bool IsRecurring { get; set; } - - /// - /// Gets or sets the recurring days. - /// - /// The recurring days. - public List RecurringDays { get; set; } - - public TimerInfo() - { - RecurringDays = new List(); - } } } diff --git a/MediaBrowser.Model/LiveTv/RecordingStatus.cs b/MediaBrowser.Model/LiveTv/RecordingStatus.cs index b8af8f6e22..7789773407 100644 --- a/MediaBrowser.Model/LiveTv/RecordingStatus.cs +++ b/MediaBrowser.Model/LiveTv/RecordingStatus.cs @@ -10,4 +10,11 @@ namespace MediaBrowser.Model.LiveTv Conflicted, Deleted } + + public enum RecurrenceType + { + Manual, + NewProgramEvents, + AllProgramEvents + } } diff --git a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs index 6a8339031a..ddefce59e2 100644 --- a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs +++ b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs @@ -1,11 +1,10 @@ using System; -using System.Collections.Generic; namespace MediaBrowser.Model.LiveTv { public class TimerInfoDto { - /// + /// /// Id of the recording. /// public string Id { get; set; } @@ -15,7 +14,7 @@ namespace MediaBrowser.Model.LiveTv /// /// The external identifier. public string ExternalId { get; set; } - + /// /// ChannelId of the recording. /// @@ -26,6 +25,12 @@ namespace MediaBrowser.Model.LiveTv /// public string ChannelName { get; set; } + /// + /// Gets or sets the program identifier. + /// + /// The program identifier. + public string ProgramId { get; set; } + /// /// Name of the recording. /// @@ -57,16 +62,5 @@ namespace MediaBrowser.Model.LiveTv /// /// true if this instance is recurring; otherwise, false. public bool IsRecurring { get; set; } - - /// - /// Gets or sets the recurring days. - /// - /// The recurring days. - public List RecurringDays { get; set; } - - public TimerInfoDto() - { - RecurringDays = new List(); - } - } + } } diff --git a/MediaBrowser.Model/Querying/EpisodeQuery.cs b/MediaBrowser.Model/Querying/EpisodeQuery.cs index 406ac9844f..56c50da7f7 100644 --- a/MediaBrowser.Model/Querying/EpisodeQuery.cs +++ b/MediaBrowser.Model/Querying/EpisodeQuery.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Model.Entities; - + namespace MediaBrowser.Model.Querying { public class EpisodeQuery @@ -8,7 +7,9 @@ namespace MediaBrowser.Model.Querying public string SeriesId { get; set; } - public LocationType[] ExcludeLocationTypes { get; set; } + public bool? IsMissing { get; set; } + + public bool? IsVirtualUnaired { get; set; } public int? SeasonNumber { get; set; } @@ -17,7 +18,6 @@ namespace MediaBrowser.Model.Querying public EpisodeQuery() { Fields = new ItemFields[] { }; - ExcludeLocationTypes = new LocationType[] { }; } } @@ -27,7 +27,9 @@ namespace MediaBrowser.Model.Querying public string SeriesId { get; set; } - public LocationType[] ExcludeLocationTypes { get; set; } + public bool? IsMissing { get; set; } + + public bool? IsVirtualUnaired { get; set; } public ItemFields[] Fields { get; set; } @@ -36,7 +38,6 @@ namespace MediaBrowser.Model.Querying public SeasonQuery() { Fields = new ItemFields[] { }; - ExcludeLocationTypes = new LocationType[] { }; } } } diff --git a/MediaBrowser.Model/Tasks/TaskInfo.cs b/MediaBrowser.Model/Tasks/TaskInfo.cs index dee4fea7fc..a7d500303d 100644 --- a/MediaBrowser.Model/Tasks/TaskInfo.cs +++ b/MediaBrowser.Model/Tasks/TaskInfo.cs @@ -56,6 +56,12 @@ namespace MediaBrowser.Model.Tasks /// The category. public string Category { get; set; } + /// + /// Gets or sets a value indicating whether this instance is hidden. + /// + /// true if this instance is hidden; otherwise, false. + public bool IsHidden { get; set; } + /// /// Initializes a new instance of the class. /// diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 00ac83f15c..4d3d877889 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -397,6 +397,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv Status = info.Status }; + if (!string.IsNullOrEmpty(info.ProgramId)) + { + dto.ProgramId = GetInternalProgramIdId(service.Name, info.ProgramId).ToString("N"); + } + return dto; } @@ -503,13 +508,61 @@ namespace MediaBrowser.Server.Implementations.LiveTv ExternalId = info.Id, ChannelId = GetInternalChannelId(service.Name, info.ChannelId).ToString("N"), Status = info.Status, - IsRecurring = info.IsRecurring, - RecurringDays = info.RecurringDays + IsRecurring = info.IsRecurring }; + if (!string.IsNullOrEmpty(info.ProgramId)) + { + dto.ProgramId = GetInternalProgramIdId(service.Name, info.ProgramId).ToString("N"); + } + return dto; } + public async Task DeleteRecording(string recordingId) + { + var recordings = await GetRecordings(new RecordingQuery + { + + }, CancellationToken.None).ConfigureAwait(false); + + var recording = recordings.Items + .FirstOrDefault(i => string.Equals(recordingId, i.Id, StringComparison.OrdinalIgnoreCase)); + + if (recording == null) + { + throw new ResourceNotFoundException(string.Format("Recording with Id {0} not found", recordingId)); + } + + var channel = GetChannel(recording.ChannelId); + var service = GetServices(channel.ServiceName, null) + .First(); + + await service.DeleteRecordingAsync(recording.ExternalId, CancellationToken.None).ConfigureAwait(false); + } + + public async Task CancelTimer(string id) + { + var timers = await GetTimers(new TimerQuery + { + + }, CancellationToken.None).ConfigureAwait(false); + + var timer = timers.Items + .FirstOrDefault(i => string.Equals(id, i.Id, StringComparison.OrdinalIgnoreCase)); + + if (timer == null) + { + throw new ResourceNotFoundException(string.Format("Timer with Id {0} not found", id)); + } + + var channel = GetChannel(timer.ChannelId); + + var service = GetServices(channel.ServiceName, null) + .First(); + + await service.CancelTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false); + } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs b/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs index c3803d9bbc..00bf9e55ba 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Server.Implementations.LiveTv { - class RefreshChannelsScheduledTask : IScheduledTask + class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask { private readonly ILiveTvManager _liveTvManager; @@ -33,7 +33,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv public Task Execute(System.Threading.CancellationToken cancellationToken, IProgress progress) { - var manager = (LiveTvManager) _liveTvManager; + var manager = (LiveTvManager)_liveTvManager; return manager.RefreshChannels(progress, cancellationToken); } @@ -50,5 +50,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv new IntervalTrigger{ Interval = TimeSpan.FromHours(2)} }; } + + public bool IsHidden + { + get { return _liveTvManager.Services.Count == 0; } + } } } diff --git a/MediaBrowser.ServerApplication/Native/HttpClientFactory.cs b/MediaBrowser.ServerApplication/Native/HttpClientFactory.cs index 57f00ba03b..368c60254b 100644 --- a/MediaBrowser.ServerApplication/Native/HttpClientFactory.cs +++ b/MediaBrowser.ServerApplication/Native/HttpClientFactory.cs @@ -17,14 +17,19 @@ namespace MediaBrowser.ServerApplication.Native /// HttpClient. public static HttpClient GetHttpClient(bool enableHttpCompression) { - return new HttpClient(new WebRequestHandler + var client = new HttpClient(new WebRequestHandler { CachePolicy = new RequestCachePolicy(RequestCacheLevel.Revalidate), AutomaticDecompression = enableHttpCompression ? DecompressionMethods.Deflate : DecompressionMethods.None + }) { Timeout = TimeSpan.FromSeconds(20) }; + + client.DefaultRequestHeaders.Add("Connection", "Keep-Alive"); + + return client; } } } diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js index 36476511ec..de96c4de94 100644 --- a/MediaBrowser.WebDashboard/ApiClient.js +++ b/MediaBrowser.WebDashboard/ApiClient.js @@ -506,6 +506,20 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi }); }; + self.createLiveTvTimer = function (options) { + + if (!options) { + throw new Error("null options"); + } + + var url = self.getUrl("LiveTv/Timers", options); + + return self.ajax({ + type: "POST", + url: url + }); + }; + /** * Gets the current server status */ @@ -1019,9 +1033,11 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi /** * Gets the server's scheduled tasks */ - self.getScheduledTasks = function () { + self.getScheduledTasks = function (options) { - var url = self.getUrl("ScheduledTasks"); + options = options || {}; + + var url = self.getUrl("ScheduledTasks", options); return self.ajax({ type: "GET", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 5585e0db5e..4aca619aa3 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -284,10 +284,10 @@ PreserveNewest - + PreserveNewest - + PreserveNewest diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config index 25938a358a..a839ece186 100644 --- a/MediaBrowser.WebDashboard/packages.config +++ b/MediaBrowser.WebDashboard/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 51bdf43d66..77765ddc62 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.252 + 3.0.253 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 6ccfca542e..ac6b606cda 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.252 + 3.0.253 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index de9b90a461..a6c1bee112 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.252 + 3.0.253 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - + From 0c398a567041e5de37837b71f16d00fbc2913d3c Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 29 Nov 2013 13:44:51 -0500 Subject: [PATCH 39/67] added create live tv timer page --- MediaBrowser.Model/LiveTv/ChannelInfoDto.cs | 19 +++++++++++++------ .../LiveTv/LiveTvManager.cs | 8 +++++++- .../Api/DashboardService.cs | 1 + .../MediaBrowser.WebDashboard.csproj | 6 ++++++ 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs b/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs index f1d550e77a..020771e5ee 100644 --- a/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs +++ b/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs @@ -1,5 +1,7 @@ -using System; -using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using System; +using System.Collections.Generic; namespace MediaBrowser.Model.LiveTv { @@ -19,12 +21,12 @@ namespace MediaBrowser.Model.LiveTv /// /// The identifier. public string Id { get; set; } - + /// - /// Gets or sets the logo image tag. + /// Gets or sets the image tags. /// - /// The logo image tag. - public Guid? PrimaryImageTag { get; set; } + /// The image tags. + public Dictionary ImageTags { get; set; } /// /// Gets or sets the number. @@ -61,5 +63,10 @@ namespace MediaBrowser.Model.LiveTv /// /// The user data. public UserItemDataDto UserData { get; set; } + + public ChannelInfoDto() + { + ImageTags = new Dictionary(); + } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 4d3d877889..688a4cc647 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -87,7 +87,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv ServiceName = info.ServiceName, ChannelType = info.ChannelType, Number = info.ChannelNumber, - PrimaryImageTag = GetLogoImageTag(info), Type = info.GetType().Name, Id = info.Id.ToString("N"), MediaType = info.MediaType @@ -98,6 +97,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv dto.UserData = _dtoService.GetUserItemDataDto(_userDataManager.GetUserData(user.Id, info.GetUserDataKey())); } + var imageTag = GetLogoImageTag(info); + + if (imageTag.HasValue) + { + dto.ImageTags[ImageType.Primary] = imageTag.Value; + } + return dto; } diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 69f05631f3..9076777a5b 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -483,6 +483,7 @@ namespace MediaBrowser.WebDashboard.Api "livetvchannels.js", "livetvguide.js", "livetvrecordings.js", + "livetvtimer.js", "livetvtimers.js", "loginpage.js", "logpage.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 4aca619aa3..73b281d9a4 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -347,6 +347,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest From 7ef1816b340d273280effa18fe7543d13baf9c8a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 29 Nov 2013 15:10:31 -0500 Subject: [PATCH 40/67] add links to channel pages --- .../LiveTv/LiveTvManager.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 688a4cc647..dc68670d2b 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -213,9 +213,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv }; } - private Guid GetInternalChannelId(string serviceName, string externalChannelId) + private Guid GetInternalChannelId(string serviceName, string externalChannelId, string channelName) { - var name = serviceName + externalChannelId; + var name = serviceName + externalChannelId + channelName; return name.ToLower().GetMBId(typeof(Channel)); } @@ -248,7 +248,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv isNew = true; } - var id = GetInternalChannelId(serviceName, channelInfo.Id); + var id = GetInternalChannelId(serviceName, channelInfo.Id, channelInfo.Name); var item = _itemRepo.RetrieveItem(id) as Channel; @@ -399,7 +399,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv StartDate = info.StartDate, Id = id, ExternalId = info.Id, - ChannelId = GetInternalChannelId(service.Name, info.ChannelId).ToString("N"), + ChannelId = GetInternalChannelId(service.Name, info.ChannelId, info.ChannelName).ToString("N"), Status = info.Status }; @@ -512,7 +512,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv StartDate = info.StartDate, Id = id, ExternalId = info.Id, - ChannelId = GetInternalChannelId(service.Name, info.ChannelId).ToString("N"), + ChannelId = GetInternalChannelId(service.Name, info.ChannelId, info.ChannelName).ToString("N"), Status = info.Status, IsRecurring = info.IsRecurring }; From 9b9e5fc4e48aa8d2595691b5cf0c3354f723726a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 29 Nov 2013 16:28:12 -0500 Subject: [PATCH 41/67] added timer properties for pre/post padding seconds --- MediaBrowser.Controller/LiveTv/TimerInfo.cs | 12 ++++++++++++ MediaBrowser.Model/LiveTv/TimerInfoDto.cs | 12 ++++++++++++ .../LiveTv/LiveTvManager.cs | 4 +++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/LiveTv/TimerInfo.cs b/MediaBrowser.Controller/LiveTv/TimerInfo.cs index 7401dafec9..624dc62cab 100644 --- a/MediaBrowser.Controller/LiveTv/TimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/TimerInfo.cs @@ -57,5 +57,17 @@ namespace MediaBrowser.Controller.LiveTv /// /// true if this instance is recurring; otherwise, false. public bool IsRecurring { get; set; } + + /// + /// Gets or sets the pre padding seconds. + /// + /// The pre padding seconds. + public int PrePaddingSeconds { get; set; } + + /// + /// Gets or sets the post padding seconds. + /// + /// The post padding seconds. + public int PostPaddingSeconds { get; set; } } } diff --git a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs index ddefce59e2..5d8ac20c45 100644 --- a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs +++ b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs @@ -62,5 +62,17 @@ namespace MediaBrowser.Model.LiveTv /// /// true if this instance is recurring; otherwise, false. public bool IsRecurring { get; set; } + + /// + /// Gets or sets the pre padding seconds. + /// + /// The pre padding seconds. + public int PrePaddingSeconds { get; set; } + + /// + /// Gets or sets the post padding seconds. + /// + /// The post padding seconds. + public int PostPaddingSeconds { get; set; } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index dc68670d2b..b55393213c 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -514,7 +514,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv ExternalId = info.Id, ChannelId = GetInternalChannelId(service.Name, info.ChannelId, info.ChannelName).ToString("N"), Status = info.Status, - IsRecurring = info.IsRecurring + IsRecurring = info.IsRecurring, + PrePaddingSeconds = info.PrePaddingSeconds, + PostPaddingSeconds = info.PostPaddingSeconds }; if (!string.IsNullOrEmpty(info.ProgramId)) From 0bdc8a49d5ea17417b3552d6db21239190bc4b6b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 30 Nov 2013 01:07:25 -0500 Subject: [PATCH 42/67] switch from httpclient to plain httpwebrequest --- .../BaseApplicationHost.cs | 5 +- .../HttpClientManager/HttpClientInfo.cs | 6 - .../HttpClientManager/HttpClientManager.cs | 443 ++++++++++-------- MediaBrowser.Common/Net/IHttpClient.cs | 5 + .../ApplicationHost.cs | 10 - .../MediaBrowser.ServerApplication.csproj | 1 - .../Native/HttpClientFactory.cs | 35 -- 7 files changed, 263 insertions(+), 242 deletions(-) delete mode 100644 MediaBrowser.ServerApplication/Native/HttpClientFactory.cs diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs index f1d8c94e55..becf649f44 100644 --- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs +++ b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs @@ -22,7 +22,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; -using System.Net.Http; using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -353,7 +352,7 @@ namespace MediaBrowser.Common.Implementations FileSystemManager = CreateFileSystemManager(); RegisterSingleInstance(FileSystemManager); - HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, Logger, CreateHttpClient, FileSystemManager); + HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, Logger, FileSystemManager); RegisterSingleInstance(HttpClient); NetworkManager = CreateNetworkManager(); @@ -378,8 +377,6 @@ namespace MediaBrowser.Common.Implementations return new CommonFileSystem(Logger, true); } - protected abstract HttpClient CreateHttpClient(bool enableHttpCompression); - /// /// Gets a list of types within an assembly /// This will handle situations that would normally throw an exception - such as a type within the assembly that depends on some other non-existant reference diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientInfo.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientInfo.cs index 33f7079df3..8af6ef6c6e 100644 --- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientInfo.cs +++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientInfo.cs @@ -1,5 +1,4 @@ using System; -using System.Net.Http; namespace MediaBrowser.Common.Implementations.HttpClientManager { @@ -8,11 +7,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager /// public class HttpClientInfo { - /// - /// Gets or sets the HTTP client. - /// - /// The HTTP client. - public HttpClient HttpClient { get; set; } /// /// Gets or sets the last timeout. /// diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs index 181c83fd3a..ea1a5c7266 100644 --- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs +++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs @@ -1,5 +1,4 @@ -using System.Reflection; -using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Model.Logging; @@ -10,7 +9,10 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Net; +using System.Net.Cache; using System.Net.Http; +using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -32,9 +34,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager /// private readonly IApplicationPaths _appPaths; - public delegate HttpClient GetHttpClientHandler(bool enableHttpCompression); - - private readonly GetHttpClientHandler _getHttpClientHandler; private readonly IFileSystem _fileSystem; /// @@ -48,7 +47,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager /// or /// logger /// - public HttpClientManager(IApplicationPaths appPaths, ILogger logger, GetHttpClientHandler getHttpClientHandler, IFileSystem fileSystem) + public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem) { if (appPaths == null) { @@ -60,7 +59,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager } _logger = logger; - _getHttpClientHandler = getHttpClientHandler; _fileSystem = fileSystem; _appPaths = appPaths; } @@ -92,111 +90,59 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager if (!_httpClients.TryGetValue(key, out client)) { - client = new HttpClientInfo - { + client = new HttpClientInfo(); - HttpClient = _getHttpClientHandler(enableHttpCompression) - }; _httpClients.TryAdd(key, client); } return client; } - public async Task GetResponse(HttpRequestOptions options) - { - ValidateParams(options.Url, options.CancellationToken); - - options.CancellationToken.ThrowIfCancellationRequested(); + private PropertyInfo _httpBehaviorPropertyInfo; - var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression); + private HttpWebRequest GetRequest(HttpRequestOptions options, string method, bool enableHttpCompression) + { + var request = HttpWebRequest.CreateHttp(options.Url); - if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < 30) + if (!string.IsNullOrEmpty(options.AcceptHeader)) { - throw new HttpException(string.Format("Cancelling connection to {0} due to a previous timeout.", options.Url)) { IsTimedOut = true }; + request.Accept = options.AcceptHeader; } - using (var message = GetHttpRequestMessage(options)) - { - if (options.ResourcePool != null) - { - await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); - } - - if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < 30) - { - if (options.ResourcePool != null) - { - options.ResourcePool.Release(); - } - - throw new HttpException(string.Format("Connection to {0} timed out", options.Url)) { IsTimedOut = true }; - } - - _logger.Info("HttpClientManager.Get url: {0}", options.Url); - - try - { - options.CancellationToken.ThrowIfCancellationRequested(); - - var response = await client.HttpClient.SendAsync(message, HttpCompletionOption.ResponseContentRead, options.CancellationToken).ConfigureAwait(false); - - EnsureSuccessStatusCode(response); + request.AutomaticDecompression = enableHttpCompression ? DecompressionMethods.Deflate : DecompressionMethods.None; + request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.Revalidate); + request.ConnectionGroupName = GetHostFromUrl(options.Url); + request.KeepAlive = true; + request.Method = method; + request.Pipelined = true; + request.Timeout = 20000; - options.CancellationToken.ThrowIfCancellationRequested(); - - return new HttpResponseInfo - { - Content = await response.Content.ReadAsStreamAsync().ConfigureAwait(false), - - StatusCode = response.StatusCode, - - ContentType = response.Content.Headers.ContentType.MediaType - }; - } - catch (OperationCanceledException ex) - { - var exception = GetCancellationException(options.Url, options.CancellationToken, ex); - - var httpException = exception as HttpException; + if (!string.IsNullOrEmpty(options.UserAgent)) + { + request.UserAgent = options.UserAgent; + } - if (httpException != null && httpException.IsTimedOut) - { - client.LastTimeout = DateTime.UtcNow; - } + var sp = request.ServicePoint; - throw exception; - } - catch (HttpRequestException ex) - { - _logger.ErrorException("Error getting response from " + options.Url, ex); + if (_httpBehaviorPropertyInfo == null) + { + _httpBehaviorPropertyInfo = sp.GetType().GetProperty("HttpBehaviour", BindingFlags.Instance | BindingFlags.NonPublic); + } - throw new HttpException(ex.Message, ex); - } - catch (Exception ex) - { - _logger.ErrorException("Error getting response from " + options.Url, ex); + _httpBehaviorPropertyInfo.SetValue(sp, (byte)0, null); - throw; - } - finally - { - if (options.ResourcePool != null) - { - options.ResourcePool.Release(); - } - } - } + return request; } /// - /// Performs a GET request and returns the resulting stream + /// Gets the response internal. /// /// The options. - /// Task{Stream}. - /// - /// - public async Task Get(HttpRequestOptions options) + /// The HTTP method. + /// Task{HttpResponseInfo}. + /// + /// + public async Task GetResponse(HttpRequestOptions options) { ValidateParams(options.Url, options.CancellationToken); @@ -209,72 +155,110 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager throw new HttpException(string.Format("Cancelling connection to {0} due to a previous timeout.", options.Url)) { IsTimedOut = true }; } - using (var message = GetHttpRequestMessage(options)) + var httpWebRequest = GetRequest(options, "GET", options.EnableHttpCompression); + + if (options.ResourcePool != null) + { + await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); + } + + if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < 30) { if (options.ResourcePool != null) { - await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); + options.ResourcePool.Release(); } - if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < 30) - { - if (options.ResourcePool != null) - { - options.ResourcePool.Release(); - } + throw new HttpException(string.Format("Connection to {0} timed out", options.Url)) { IsTimedOut = true }; + } - throw new HttpException(string.Format("Connection to {0} timed out", options.Url)) { IsTimedOut = true }; - } + _logger.Info("HttpClientManager.GET url: {0}", options.Url); - _logger.Info("HttpClientManager.Get url: {0}", options.Url); + try + { + options.CancellationToken.ThrowIfCancellationRequested(); - try + using (var response = await httpWebRequest.GetResponseAsync().ConfigureAwait(false)) { + var httpResponse = (HttpWebResponse)response; + + EnsureSuccessStatusCode(httpResponse); + options.CancellationToken.ThrowIfCancellationRequested(); - var response = await client.HttpClient.SendAsync(message, HttpCompletionOption.ResponseContentRead, options.CancellationToken).ConfigureAwait(false); + using (var stream = httpResponse.GetResponseStream()) + { + var memoryStream = new MemoryStream(); - EnsureSuccessStatusCode(response); + await stream.CopyToAsync(memoryStream).ConfigureAwait(false); - options.CancellationToken.ThrowIfCancellationRequested(); + memoryStream.Position = 0; - return await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - } - catch (OperationCanceledException ex) - { - var exception = GetCancellationException(options.Url, options.CancellationToken, ex); + return new HttpResponseInfo + { + Content = memoryStream, - var httpException = exception as HttpException; + StatusCode = httpResponse.StatusCode, - if (httpException != null && httpException.IsTimedOut) - { - client.LastTimeout = DateTime.UtcNow; + ContentType = httpResponse.ContentType + }; } - - throw exception; } - catch (HttpRequestException ex) - { - _logger.ErrorException("Error getting response from " + options.Url, ex); + } + catch (OperationCanceledException ex) + { + var exception = GetCancellationException(options.Url, options.CancellationToken, ex); - throw new HttpException(ex.Message, ex); - } - catch (Exception ex) - { - _logger.ErrorException("Error getting response from " + options.Url, ex); + var httpException = exception as HttpException; - throw; + if (httpException != null && httpException.IsTimedOut) + { + client.LastTimeout = DateTime.UtcNow; } - finally + + throw exception; + } + catch (HttpRequestException ex) + { + _logger.ErrorException("Error getting response from " + options.Url, ex); + + throw new HttpException(ex.Message, ex); + } + catch (WebException ex) + { + _logger.ErrorException("Error getting response from " + options.Url, ex); + + throw new HttpException(ex.Message, ex); + } + catch (Exception ex) + { + _logger.ErrorException("Error getting response from " + options.Url, ex); + + throw; + } + finally + { + if (options.ResourcePool != null) { - if (options.ResourcePool != null) - { - options.ResourcePool.Release(); - } + options.ResourcePool.Release(); } } } + /// + /// Performs a GET request and returns the resulting stream + /// + /// The options. + /// Task{Stream}. + /// + /// + public async Task Get(HttpRequestOptions options) + { + var response = await GetResponse(options).ConfigureAwait(false); + + return response.Content; + } + /// /// Performs a GET request and returns the resulting stream /// @@ -306,64 +290,112 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager /// /// Performs a POST request /// - /// The URL. + /// The options. /// Params to add to the POST data. - /// The resource pool. - /// The cancellation token. /// stream on success, null on failure + /// + /// /// postData /// - public async Task Post(string url, Dictionary postData, SemaphoreSlim resourcePool, CancellationToken cancellationToken) + public async Task Post(HttpRequestOptions options, Dictionary postData) { - ValidateParams(url, cancellationToken); + ValidateParams(options.Url, options.CancellationToken); - if (postData == null) - { - throw new ArgumentNullException("postData"); - } + options.CancellationToken.ThrowIfCancellationRequested(); - cancellationToken.ThrowIfCancellationRequested(); + var httpWebRequest = GetRequest(options, "POST", options.EnableHttpCompression); var strings = postData.Keys.Select(key => string.Format("{0}={1}", key, postData[key])); var postContent = string.Join("&", strings.ToArray()); - var content = new StringContent(postContent, Encoding.UTF8, "application/x-www-form-urlencoded"); + var bytes = Encoding.UTF8.GetBytes(postContent); + + httpWebRequest.ContentType = "application/x-www-form-urlencoded"; + httpWebRequest.ContentLength = bytes.Length; + httpWebRequest.GetRequestStream().Write(bytes, 0, bytes.Length); - if (resourcePool != null) + if (options.ResourcePool != null) { - await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); } - _logger.Info("HttpClientManager.Post url: {0}", url); + _logger.Info("HttpClientManager.POST url: {0}", options.Url); try { - cancellationToken.ThrowIfCancellationRequested(); + options.CancellationToken.ThrowIfCancellationRequested(); + + using (var response = await httpWebRequest.GetResponseAsync().ConfigureAwait(false)) + { + var httpResponse = (HttpWebResponse)response; + + EnsureSuccessStatusCode(httpResponse); + + options.CancellationToken.ThrowIfCancellationRequested(); - var msg = await GetHttpClient(GetHostFromUrl(url), true).HttpClient.PostAsync(url, content, cancellationToken).ConfigureAwait(false); + using (var stream = httpResponse.GetResponseStream()) + { + var memoryStream = new MemoryStream(); + + await stream.CopyToAsync(memoryStream).ConfigureAwait(false); - EnsureSuccessStatusCode(msg); + memoryStream.Position = 0; - return await msg.Content.ReadAsStreamAsync().ConfigureAwait(false); + return memoryStream; + } + } } catch (OperationCanceledException ex) { - throw GetCancellationException(url, cancellationToken, ex); + var exception = GetCancellationException(options.Url, options.CancellationToken, ex); + + throw exception; } catch (HttpRequestException ex) { - _logger.ErrorException("Error getting response from " + url, ex); + _logger.ErrorException("Error getting response from " + options.Url, ex); + + throw new HttpException(ex.Message, ex); + } + catch (WebException ex) + { + _logger.ErrorException("Error getting response from " + options.Url, ex); throw new HttpException(ex.Message, ex); } + catch (Exception ex) + { + _logger.ErrorException("Error getting response from " + options.Url, ex); + + throw; + } finally { - if (resourcePool != null) + if (options.ResourcePool != null) { - resourcePool.Release(); + options.ResourcePool.Release(); } } } + /// + /// Performs a POST request + /// + /// The URL. + /// Params to add to the POST data. + /// The resource pool. + /// The cancellation token. + /// stream on success, null on failure + public Task Post(string url, Dictionary postData, SemaphoreSlim resourcePool, CancellationToken cancellationToken) + { + return Post(new HttpRequestOptions + { + Url = url, + ResourcePool = resourcePool, + CancellationToken = cancellationToken + + }, postData); + } + /// /// Downloads the contents of a given url into a temporary location /// @@ -392,6 +424,8 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager options.CancellationToken.ThrowIfCancellationRequested(); + var httpWebRequest = GetRequest(options, "GET", options.EnableHttpCompression); + if (options.ResourcePool != null) { await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); @@ -399,60 +433,79 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager options.Progress.Report(0); - _logger.Info("HttpClientManager.GetTempFile url: {0}, temp file: {1}", options.Url, tempFile); + _logger.Info("HttpClientManager.GetTempFileResponse url: {0}", options.Url); try { options.CancellationToken.ThrowIfCancellationRequested(); - using (var message = GetHttpRequestMessage(options)) + using (var response = await httpWebRequest.GetResponseAsync().ConfigureAwait(false)) { - using (var response = await GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression).HttpClient.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).ConfigureAwait(false)) - { - EnsureSuccessStatusCode(response); + var httpResponse = (HttpWebResponse)response; - options.CancellationToken.ThrowIfCancellationRequested(); + EnsureSuccessStatusCode(httpResponse); - var contentLength = GetContentLength(response); + options.CancellationToken.ThrowIfCancellationRequested(); + + var contentLength = GetContentLength(httpResponse); - if (!contentLength.HasValue) + if (!contentLength.HasValue) + { + // We're not able to track progress + using (var stream = httpResponse.GetResponseStream()) { - // We're not able to track progress - using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) + using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { - using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true)) - { - await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); - } + await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); } } - else + } + else + { + using (var stream = ProgressStream.CreateReadProgressStream(httpResponse.GetResponseStream(), options.Progress.Report, contentLength.Value)) { - using (var stream = ProgressStream.CreateReadProgressStream(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), options.Progress.Report, contentLength.Value)) + using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { - using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true)) - { - await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); - } + await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); } } + } - options.Progress.Report(100); + options.Progress.Report(100); - return new HttpResponseInfo - { - TempFilePath = tempFile, + return new HttpResponseInfo + { + TempFilePath = tempFile, - StatusCode = response.StatusCode, + StatusCode = httpResponse.StatusCode, - ContentType = response.Content.Headers.ContentType.MediaType - }; - } + ContentType = httpResponse.ContentType + }; } } + catch (OperationCanceledException ex) + { + var exception = GetCancellationException(options.Url, options.CancellationToken, ex); + + throw exception; + } + catch (HttpRequestException ex) + { + _logger.ErrorException("Error getting response from " + options.Url, ex); + + throw new HttpException(ex.Message, ex); + } + catch (WebException ex) + { + _logger.ErrorException("Error getting response from " + options.Url, ex); + + throw new HttpException(ex.Message, ex); + } catch (Exception ex) { - throw GetTempFileException(ex, options, tempFile); + _logger.ErrorException("Error getting response from " + options.Url, ex); + + throw; } finally { @@ -522,6 +575,18 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager return long.Parse(string.Join(string.Empty, lengthValues.ToArray()), UsCulture); } + private long? GetContentLength(HttpWebResponse response) + { + var length = response.ContentLength; + + if (length == 0) + { + return null; + } + + return length; + } + protected static readonly CultureInfo UsCulture = new CultureInfo("en-US"); /// @@ -614,11 +679,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager { if (dispose) { - foreach (var client in _httpClients.Values.ToList()) - { - client.HttpClient.Dispose(); - } - _httpClients.Clear(); } } @@ -659,6 +719,17 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager } } + private void EnsureSuccessStatusCode(HttpWebResponse response) + { + var statusCode = response.StatusCode; + var isSuccessful = statusCode >= HttpStatusCode.OK && statusCode <= (HttpStatusCode)299; + + if (!isSuccessful) + { + throw new HttpException(response.StatusDescription) { StatusCode = response.StatusCode }; + } + } + /// /// Posts the specified URL. /// diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs index 93348d4ca0..54d6665e23 100644 --- a/MediaBrowser.Common/Net/IHttpClient.cs +++ b/MediaBrowser.Common/Net/IHttpClient.cs @@ -73,6 +73,11 @@ namespace MediaBrowser.Common.Net /// Task GetTempFile(HttpRequestOptions options); + /// + /// Gets the temporary file response. + /// + /// The options. + /// Task{HttpResponseInfo}. Task GetTempFileResponse(HttpRequestOptions options); } } \ No newline at end of file diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index fcd7d299cf..be865881af 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -702,16 +702,6 @@ namespace MediaBrowser.ServerApplication OnApplicationUpdated(package.version); } - /// - /// Creates the HTTP client. - /// - /// if set to true [enable HTTP compression]. - /// HttpClient. - protected override HttpClient CreateHttpClient(bool enableHttpCompression) - { - return HttpClientFactory.GetHttpClient(enableHttpCompression); - } - protected override void ConfigureAutoRunAtStartup(bool autorun) { Autorun.Configure(autorun); diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index cfacffe087..795799ca3e 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -190,7 +190,6 @@ - diff --git a/MediaBrowser.ServerApplication/Native/HttpClientFactory.cs b/MediaBrowser.ServerApplication/Native/HttpClientFactory.cs deleted file mode 100644 index 368c60254b..0000000000 --- a/MediaBrowser.ServerApplication/Native/HttpClientFactory.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Net; -using System.Net.Cache; -using System.Net.Http; - -namespace MediaBrowser.ServerApplication.Native -{ - /// - /// Class HttpClientFactory - /// - public static class HttpClientFactory - { - /// - /// Gets the HTTP client. - /// - /// if set to true [enable HTTP compression]. - /// HttpClient. - public static HttpClient GetHttpClient(bool enableHttpCompression) - { - var client = new HttpClient(new WebRequestHandler - { - CachePolicy = new RequestCachePolicy(RequestCacheLevel.Revalidate), - AutomaticDecompression = enableHttpCompression ? DecompressionMethods.Deflate : DecompressionMethods.None - - }) - { - Timeout = TimeSpan.FromSeconds(20) - }; - - client.DefaultRequestHeaders.Add("Connection", "Keep-Alive"); - - return client; - } - } -} From f05ea5d20f2526c7090516a2e8cef31e94473826 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 30 Nov 2013 01:08:14 -0500 Subject: [PATCH 43/67] prevent providers from attempting image downloads over and over again --- .../ImageFromMediaLocationProvider.cs | 4 +- .../MediaInfo/AudioImageProvider.cs | 2 + .../MediaInfo/VideoImageProvider.cs | 2 + .../Movies/FanArtMovieProvider.cs | 64 ++++++++----------- .../Music/FanArtAlbumProvider.cs | 30 ++++++--- .../Music/FanArtArtistProvider.cs | 50 ++++++++------- .../TV/FanArtSeasonProvider.cs | 23 ++++++- MediaBrowser.Providers/TV/FanArtTVProvider.cs | 57 ++++++++--------- 8 files changed, 128 insertions(+), 104 deletions(-) diff --git a/MediaBrowser.Providers/ImageFromMediaLocationProvider.cs b/MediaBrowser.Providers/ImageFromMediaLocationProvider.cs index 65eee0f3a6..f278a90893 100644 --- a/MediaBrowser.Providers/ImageFromMediaLocationProvider.cs +++ b/MediaBrowser.Providers/ImageFromMediaLocationProvider.cs @@ -101,8 +101,6 @@ namespace MediaBrowser.Providers { cancellationToken.ThrowIfCancellationRequested(); - var args = GetResolveArgsContainingImages(item); - // Make sure current image paths still exist item.ValidateImages(); @@ -114,6 +112,8 @@ namespace MediaBrowser.Providers cancellationToken.ThrowIfCancellationRequested(); + var args = GetResolveArgsContainingImages(item); + PopulateBaseItemImages(item, args); SetLastRefreshed(item, DateTime.UtcNow); diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index f61057fe6c..683dd42310 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -123,6 +123,8 @@ namespace MediaBrowser.Providers.MediaInfo /// Task{System.Boolean}. public override async Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) { + item.ValidateImages(); + var audio = (Audio)item; if (string.IsNullOrEmpty(audio.PrimaryImagePath) && audio.MediaStreams.Any(s => s.Type == MediaStreamType.Video)) diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs index 7aea484fd8..b577420428 100644 --- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs @@ -173,6 +173,8 @@ namespace MediaBrowser.Providers.MediaInfo /// Task{System.Boolean}. public override async Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) { + item.ValidateImages(); + var video = (Video)item; // Double check this here in case force was used diff --git a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs b/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs index 39908c3eef..be195d6d2e 100644 --- a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs +++ b/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs @@ -15,6 +15,8 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Net; +using System.Net; namespace MediaBrowser.Providers.Movies { @@ -253,72 +255,42 @@ namespace MediaBrowser.Providers.Movies if (ConfigurationManager.Configuration.DownloadMovieImages.Primary && !item.HasImage(ImageType.Primary)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Primary); - - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); - } + await SaveImage(item, images, ImageType.Primary, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); if (ConfigurationManager.Configuration.DownloadMovieImages.Logo && !item.HasImage(ImageType.Logo)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Logo); - - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Logo, null, cancellationToken).ConfigureAwait(false); - } + await SaveImage(item, images, ImageType.Logo, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); if (ConfigurationManager.Configuration.DownloadMovieImages.Art && !item.HasImage(ImageType.Art)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Art); - - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Art, null, cancellationToken).ConfigureAwait(false); - } + await SaveImage(item, images, ImageType.Art, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); if (ConfigurationManager.Configuration.DownloadMovieImages.Disc && !item.HasImage(ImageType.Disc)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Disc); - - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Disc, null, cancellationToken).ConfigureAwait(false); - } + await SaveImage(item, images, ImageType.Disc, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); if (ConfigurationManager.Configuration.DownloadMovieImages.Banner && !item.HasImage(ImageType.Banner)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Banner); - - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Banner, null, cancellationToken).ConfigureAwait(false); - } + await SaveImage(item, images, ImageType.Banner, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); if (ConfigurationManager.Configuration.DownloadMovieImages.Thumb && !item.HasImage(ImageType.Thumb)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Thumb); - - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Thumb, null, cancellationToken).ConfigureAwait(false); - } + await SaveImage(item, images, ImageType.Thumb, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); @@ -336,5 +308,25 @@ namespace MediaBrowser.Providers.Movies } } } + + private async Task SaveImage(BaseItem item, List images, ImageType type, CancellationToken cancellationToken) + { + foreach (var image in images.Where(i => i.Type == type)) + { + try + { + await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, type, null, cancellationToken).ConfigureAwait(false); + break; + } + catch (HttpException ex) + { + // Sometimes fanart has bad url's in their xml + if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) + { + continue; + } + } + } + } } } diff --git a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs index f67e2b5091..a49de9a29e 100644 --- a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs @@ -14,6 +14,8 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Net; +using System.Net; namespace MediaBrowser.Providers.Music { @@ -174,23 +176,33 @@ namespace MediaBrowser.Providers.Music if (ConfigurationManager.Configuration.DownloadMusicAlbumImages.Primary && !item.HasImage(ImageType.Primary)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Primary); - - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); - } + await SaveImage(item, images, ImageType.Primary, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); if (ConfigurationManager.Configuration.DownloadMusicAlbumImages.Disc && !item.HasImage(ImageType.Disc)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Disc); + await SaveImage(item, images, ImageType.Disc, cancellationToken).ConfigureAwait(false); + } + } - if (image != null) + private async Task SaveImage(BaseItem item, List images, ImageType type, CancellationToken cancellationToken) + { + foreach (var image in images.Where(i => i.Type == type)) + { + try + { + await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, type, null, cancellationToken).ConfigureAwait(false); + break; + } + catch (HttpException ex) { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Disc, null, cancellationToken).ConfigureAwait(false); + // Sometimes fanart has bad url's in their xml + if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) + { + continue; + } } } } diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs index 6df21b61d9..1b0a80d9a9 100644 --- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs +++ b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs @@ -16,6 +16,8 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Net; +using System.Net; namespace MediaBrowser.Providers.Music { @@ -270,48 +272,28 @@ namespace MediaBrowser.Providers.Music if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Primary && !item.HasImage(ImageType.Primary)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Primary); - - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); - } + await SaveImage(item, images, ImageType.Primary, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Logo && !item.HasImage(ImageType.Logo)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Logo); - - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Logo, null, cancellationToken).ConfigureAwait(false); - } + await SaveImage(item, images, ImageType.Logo, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Art && !item.HasImage(ImageType.Art)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Art); - - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Art, null, cancellationToken).ConfigureAwait(false); - } + await SaveImage(item, images, ImageType.Art, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Banner && !item.HasImage(ImageType.Banner)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Banner); - - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Banner, null, cancellationToken).ConfigureAwait(false); - } + await SaveImage(item, images, ImageType.Banner, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); @@ -329,5 +311,25 @@ namespace MediaBrowser.Providers.Music } } } + + private async Task SaveImage(BaseItem item, List images, ImageType type, CancellationToken cancellationToken) + { + foreach (var image in images.Where(i => i.Type == type)) + { + try + { + await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, type, null, cancellationToken).ConfigureAwait(false); + break; + } + catch (HttpException ex) + { + // Sometimes fanart has bad url's in their xml + if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) + { + continue; + } + } + } + } } } diff --git a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs index 2a5b16bef4..bf52ca349c 100644 --- a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs @@ -13,6 +13,8 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Net; +using System.Net; namespace MediaBrowser.Providers.TV { @@ -121,11 +123,26 @@ namespace MediaBrowser.Providers.TV { if (ConfigurationManager.Configuration.DownloadSeasonImages.Thumb && !season.HasImage(ImageType.Thumb)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Thumb); + await SaveImage(season, images, ImageType.Thumb, cancellationToken).ConfigureAwait(false); + } + } - if (image != null) + private async Task SaveImage(BaseItem item, List images, ImageType type, CancellationToken cancellationToken) + { + foreach (var image in images.Where(i => i.Type == type)) + { + try + { + await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, type, null, cancellationToken).ConfigureAwait(false); + break; + } + catch (HttpException ex) { - await _providerManager.SaveImage(season, image.Url, FanArtResourcePool, ImageType.Thumb, null, cancellationToken).ConfigureAwait(false); + // Sometimes fanart has bad url's in their xml + if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) + { + continue; + } } } } diff --git a/MediaBrowser.Providers/TV/FanArtTVProvider.cs b/MediaBrowser.Providers/TV/FanArtTVProvider.cs index 1f20140c50..037e0b5bc5 100644 --- a/MediaBrowser.Providers/TV/FanArtTVProvider.cs +++ b/MediaBrowser.Providers/TV/FanArtTVProvider.cs @@ -16,6 +16,8 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Net; +using System.Net; namespace MediaBrowser.Providers.TV { @@ -198,60 +200,35 @@ namespace MediaBrowser.Providers.TV if (ConfigurationManager.Configuration.DownloadSeriesImages.Primary && !item.HasImage(ImageType.Primary)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Primary); - - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); - } + await SaveImage(item, images, ImageType.Primary, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); if (ConfigurationManager.Configuration.DownloadSeriesImages.Logo && !item.HasImage(ImageType.Logo)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Logo); - - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Logo, null, cancellationToken).ConfigureAwait(false); - } + await SaveImage(item, images, ImageType.Logo, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); if (ConfigurationManager.Configuration.DownloadSeriesImages.Art && !item.HasImage(ImageType.Art)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Art); - - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Art, null, cancellationToken).ConfigureAwait(false); - } + await SaveImage(item, images, ImageType.Art, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); if (ConfigurationManager.Configuration.DownloadSeriesImages.Thumb && !item.HasImage(ImageType.Thumb)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Thumb); - - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Thumb, null, cancellationToken).ConfigureAwait(false); - } + await SaveImage(item, images, ImageType.Thumb, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); if (ConfigurationManager.Configuration.DownloadSeriesImages.Banner && !item.HasImage(ImageType.Banner)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Banner); - - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Banner, null, cancellationToken).ConfigureAwait(false); - } + await SaveImage(item, images, ImageType.Banner, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); @@ -271,6 +248,26 @@ namespace MediaBrowser.Providers.TV } + private async Task SaveImage(BaseItem item, List images, ImageType type, CancellationToken cancellationToken) + { + foreach (var image in images.Where(i => i.Type == type)) + { + try + { + await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, type, null, cancellationToken).ConfigureAwait(false); + break; + } + catch (HttpException ex) + { + // Sometimes fanart has bad url's in their xml + if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) + { + continue; + } + } + } + } + /// /// Downloads the series XML. /// From 45a4d25e2615e5ec05befc61560ad54b419504eb Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 30 Nov 2013 01:49:38 -0500 Subject: [PATCH 44/67] updated live tv methods + nuget --- .../HttpClientManager/HttpClientManager.cs | 13 +++- .../LiveTv/ILiveTvService.cs | 7 ++ .../LiveTv/RecurringTimerInfo.cs | 73 +++++++++++++++++++ MediaBrowser.Controller/LiveTv/TimerInfo.cs | 12 +-- .../MediaBrowser.Controller.csproj | 1 + MediaBrowser.Model/LiveTv/RecordingStatus.cs | 6 +- MediaBrowser.Model/LiveTv/TimerInfoDto.cs | 6 +- .../LiveTv/LiveTvManager.cs | 2 +- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- 11 files changed, 109 insertions(+), 21 deletions(-) create mode 100644 MediaBrowser.Controller/LiveTv/RecurringTimerInfo.cs diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs index ea1a5c7266..8d80185add 100644 --- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs +++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs @@ -24,6 +24,11 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager /// public class HttpClientManager : IHttpClient { + /// + /// When one request to a host times out, we'll ban all other requests for this period of time, to prevent scans from stalling + /// + private int TimeoutSeconds = 30; + /// /// The _logger /// @@ -122,13 +127,13 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager request.UserAgent = options.UserAgent; } + // This is a hack to prevent KeepAlive from getting disabled internally by the HttpWebRequest + // May need to remove this for mono var sp = request.ServicePoint; - if (_httpBehaviorPropertyInfo == null) { _httpBehaviorPropertyInfo = sp.GetType().GetProperty("HttpBehaviour", BindingFlags.Instance | BindingFlags.NonPublic); } - _httpBehaviorPropertyInfo.SetValue(sp, (byte)0, null); return request; @@ -150,7 +155,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression); - if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < 30) + if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < TimeoutSeconds) { throw new HttpException(string.Format("Cancelling connection to {0} due to a previous timeout.", options.Url)) { IsTimedOut = true }; } @@ -162,7 +167,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); } - if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < 30) + if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < TimeoutSeconds) { if (options.ResourcePool != null) { diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs index 9bc032af35..2b58d8bc56 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs @@ -68,6 +68,13 @@ namespace MediaBrowser.Controller.LiveTv /// The cancellation token. /// Task{IEnumerable{RecordingInfo}}. Task> GetTimersAsync(CancellationToken cancellationToken); + + /// + /// Gets the recurring timers asynchronous. + /// + /// The cancellation token. + /// Task{IEnumerable{RecurringTimerInfo}}. + Task> GetRecurringTimersAsync(CancellationToken cancellationToken); /// /// Gets the programs asynchronous. diff --git a/MediaBrowser.Controller/LiveTv/RecurringTimerInfo.cs b/MediaBrowser.Controller/LiveTv/RecurringTimerInfo.cs new file mode 100644 index 0000000000..08a8b1f92f --- /dev/null +++ b/MediaBrowser.Controller/LiveTv/RecurringTimerInfo.cs @@ -0,0 +1,73 @@ +using MediaBrowser.Model.LiveTv; +using System; + +namespace MediaBrowser.Controller.LiveTv +{ + public class RecurringTimerInfo + { + /// + /// Id of the recording. + /// + public string Id { get; set; } + + /// + /// ChannelId of the recording. + /// + public string ChannelId { get; set; } + + /// + /// ChannelName of the recording. + /// + public string ChannelName { get; set; } + + /// + /// Gets or sets the program identifier. + /// + /// The program identifier. + public string ProgramId { get; set; } + + /// + /// Name of the recording. + /// + public string Name { get; set; } + + /// + /// Description of the recording. + /// + public string Description { get; set; } + + /// + /// The start date of the recording, in UTC. + /// + public DateTime StartDate { get; set; } + + /// + /// The end date of the recording, in UTC. + /// + public DateTime EndDate { get; set; } + + /// + /// Gets or sets the status. + /// + /// The status. + public RecordingStatus Status { get; set; } + + /// + /// Gets or sets the pre padding seconds. + /// + /// The pre padding seconds. + public int PrePaddingSeconds { get; set; } + + /// + /// Gets or sets the post padding seconds. + /// + /// The post padding seconds. + public int PostPaddingSeconds { get; set; } + + /// + /// Gets or sets the type of the recurrence. + /// + /// The type of the recurrence. + public RecurrenceType RecurrenceType { get; set; } + } +} diff --git a/MediaBrowser.Controller/LiveTv/TimerInfo.cs b/MediaBrowser.Controller/LiveTv/TimerInfo.cs index 624dc62cab..0fe7c34f2d 100644 --- a/MediaBrowser.Controller/LiveTv/TimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/TimerInfo.cs @@ -10,6 +10,12 @@ namespace MediaBrowser.Controller.LiveTv /// public string Id { get; set; } + /// + /// Gets or sets the recurring timer identifier. + /// + /// The recurring timer identifier. + public string RecurringTimerId { get; set; } + /// /// ChannelId of the recording. /// @@ -52,12 +58,6 @@ namespace MediaBrowser.Controller.LiveTv /// The status. public RecordingStatus Status { get; set; } - /// - /// Gets or sets a value indicating whether this instance is recurring. - /// - /// true if this instance is recurring; otherwise, false. - public bool IsRecurring { get; set; } - /// /// Gets or sets the pre padding seconds. /// diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 2068fccf83..ec0439c2c7 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -110,6 +110,7 @@ + diff --git a/MediaBrowser.Model/LiveTv/RecordingStatus.cs b/MediaBrowser.Model/LiveTv/RecordingStatus.cs index 7789773407..08a7cfb0c2 100644 --- a/MediaBrowser.Model/LiveTv/RecordingStatus.cs +++ b/MediaBrowser.Model/LiveTv/RecordingStatus.cs @@ -14,7 +14,9 @@ namespace MediaBrowser.Model.LiveTv public enum RecurrenceType { Manual, - NewProgramEvents, - AllProgramEvents + NewProgramEventsOneChannel, + AllProgramEventsOneChannel, + NewProgramEventsAllChannels, + AllProgramEventsAllChannels } } diff --git a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs index 5d8ac20c45..f7d63e9687 100644 --- a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs +++ b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs @@ -58,10 +58,10 @@ namespace MediaBrowser.Model.LiveTv public RecordingStatus Status { get; set; } /// - /// Gets or sets a value indicating whether this instance is recurring. + /// Gets or sets the recurring timer identifier. /// - /// true if this instance is recurring; otherwise, false. - public bool IsRecurring { get; set; } + /// The recurring timer identifier. + public string RecurringTimerId { get; set; } /// /// Gets or sets the pre padding seconds. diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index b55393213c..ea8ea45b69 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -514,7 +514,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv ExternalId = info.Id, ChannelId = GetInternalChannelId(service.Name, info.ChannelId, info.ChannelName).ToString("N"), Status = info.Status, - IsRecurring = info.IsRecurring, + RecurringTimerId = info.RecurringTimerId, PrePaddingSeconds = info.PrePaddingSeconds, PostPaddingSeconds = info.PostPaddingSeconds }; diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 77765ddc62..17311530fa 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.253 + 3.0.254 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index ac6b606cda..e95a638ab8 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.253 + 3.0.254 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index a6c1bee112..38bb37530e 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.253 + 3.0.254 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - + From 58f1a314b5ef3d13c2bc034f8a8949d9e88d1c20 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 30 Nov 2013 13:32:39 -0500 Subject: [PATCH 45/67] update to service stack 3.0.70.0 --- MediaBrowser.Api/MediaBrowser.Api.csproj | 21 ++-- MediaBrowser.Api/TvShowsService.cs | 23 +++- MediaBrowser.Api/packages.config | 4 +- .../HttpClientManager/HttpClientManager.cs | 112 +++--------------- ...MediaBrowser.Common.Implementations.csproj | 7 +- .../Serialization/JsonSerializer.cs | 2 + .../packages.config | 2 +- .../MediaBrowser.Common.csproj | 21 ++-- MediaBrowser.Common/packages.config | 4 +- MediaBrowser.Model/Querying/EpisodeQuery.cs | 2 + MediaBrowser.Model/System/SystemInfo.cs | 12 ++ ...MediaBrowser.Server.Implementations.csproj | 60 +++++----- .../packages.config | 10 +- .../swagger-ui/index.html | 2 +- .../ApplicationHost.cs | 2 + .../MediaBrowser.ServerApplication.csproj | 20 ++-- .../packages.config | 6 +- .../MediaBrowser.WebDashboard.csproj | 21 ++-- MediaBrowser.WebDashboard/packages.config | 4 +- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- 22 files changed, 155 insertions(+), 190 deletions(-) diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index a5720dfad1..706117fc26 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -38,6 +38,18 @@ Always + + False + ..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Common.dll + + + False + ..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll + + + False + ..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll + @@ -47,15 +59,6 @@ ..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll - - ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll - - - ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll - - - ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll - diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index 23b8efa7bd..9191bfc0c7 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -81,6 +81,9 @@ namespace MediaBrowser.Api [ApiMember(Name = "Season", Description = "Optional filter by season number.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public int? Season { get; set; } + [ApiMember(Name = "SeasonId", Description = "Optional. Filter by season id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string SeasonId { get; set; } + [ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? IsMissing { get; set; } @@ -442,7 +445,25 @@ namespace MediaBrowser.Api var sortOrder = ItemSortBy.SortName; - if (request.Season.HasValue) + if (!string.IsNullOrEmpty(request.SeasonId)) + { + var season = _libraryManager.GetItemById(request.Id) as Season; + + if (season.IndexNumber.HasValue) + { + episodes = FilterEpisodesBySeason(episodes, season.IndexNumber.Value, true); + + sortOrder = ItemSortBy.AiredEpisodeOrder; + } + else + { + episodes = season.RecursiveChildren.OfType(); + + sortOrder = ItemSortBy.SortName; + } + } + + else if (request.Season.HasValue) { episodes = FilterEpisodesBySeason(episodes, request.Season.Value, true); diff --git a/MediaBrowser.Api/packages.config b/MediaBrowser.Api/packages.config index c9fec81005..e9a27e8ad8 100644 --- a/MediaBrowser.Api/packages.config +++ b/MediaBrowser.Api/packages.config @@ -1,6 +1,6 @@  - - + + \ No newline at end of file diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs index 8d80185add..8fccb7c2a9 100644 --- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs +++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs @@ -27,7 +27,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager /// /// When one request to a host times out, we'll ban all other requests for this period of time, to prevent scans from stalling /// - private int TimeoutSeconds = 30; + private const int TimeoutSeconds = 30; /// /// The _logger @@ -42,16 +42,14 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager private readonly IFileSystem _fileSystem; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The app paths. /// The logger. - /// The get HTTP client handler. - /// - /// appPaths + /// The file system. + /// appPaths /// or - /// logger - /// + /// logger public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem) { if (appPaths == null) @@ -143,7 +141,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager /// Gets the response internal. /// /// The options. - /// The HTTP method. /// Task{HttpResponseInfo}. /// /// @@ -490,27 +487,19 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager } catch (OperationCanceledException ex) { - var exception = GetCancellationException(options.Url, options.CancellationToken, ex); - - throw exception; + throw GetTempFileException(ex, options, tempFile); } catch (HttpRequestException ex) { - _logger.ErrorException("Error getting response from " + options.Url, ex); - - throw new HttpException(ex.Message, ex); + throw GetTempFileException(ex, options, tempFile); } catch (WebException ex) { - _logger.ErrorException("Error getting response from " + options.Url, ex); - - throw new HttpException(ex.Message, ex); + throw GetTempFileException(ex, options, tempFile); } catch (Exception ex) { - _logger.ErrorException("Error getting response from " + options.Url, ex); - - throw; + throw GetTempFileException(ex, options, tempFile); } finally { @@ -521,65 +510,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager } } - /// - /// Gets the message. - /// - /// The options. - /// HttpResponseMessage. - private HttpRequestMessage GetHttpRequestMessage(HttpRequestOptions options) - { - var message = new HttpRequestMessage(HttpMethod.Get, options.Url); - - foreach (var pair in options.RequestHeaders.ToList()) - { - if (!message.Headers.TryAddWithoutValidation(pair.Key, pair.Value)) - { - _logger.Error("Unable to add request header {0} with value {1}", pair.Key, pair.Value); - } - } - - return message; - } - - /// - /// Gets the length of the content. - /// - /// The response. - /// System.Nullable{System.Int64}. - private long? GetContentLength(HttpResponseMessage response) - { - IEnumerable lengthValues = null; - - // Seeing some InvalidOperationException here under mono - try - { - response.Headers.TryGetValues("content-length", out lengthValues); - } - catch (InvalidOperationException ex) - { - _logger.ErrorException("Error accessing response.Headers.TryGetValues Content-Length", ex); - } - - if (lengthValues == null) - { - try - { - response.Content.Headers.TryGetValues("content-length", out lengthValues); - } - catch (InvalidOperationException ex) - { - _logger.ErrorException("Error accessing response.Content.Headers.TryGetValues Content-Length", ex); - } - } - - if (lengthValues == null) - { - return null; - } - - return long.Parse(string.Join(string.Empty, lengthValues.ToArray()), UsCulture); - } - private long? GetContentLength(HttpWebResponse response) { var length = response.ContentLength; @@ -616,16 +546,23 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager _logger.ErrorException("Error getting response from " + options.Url, ex); - var httpRequestException = ex as HttpRequestException; - // Cleanup DeleteTempFile(tempFile); + var httpRequestException = ex as HttpRequestException; + if (httpRequestException != null) { return new HttpException(ex.Message, ex); } + var webException = ex as WebException; + + if (webException != null) + { + return new HttpException(ex.Message, ex); + } + return ex; } @@ -711,19 +648,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager return exception; } - /// - /// Ensures the success status code. - /// - /// The response. - /// - private void EnsureSuccessStatusCode(HttpResponseMessage response) - { - if (!response.IsSuccessStatusCode) - { - throw new HttpException(response.ReasonPhrase) { StatusCode = response.StatusCode }; - } - } - private void EnsureSuccessStatusCode(HttpWebResponse response) { var statusCode = response.StatusCode; diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj index 9e48f3b3e9..9487855756 100644 --- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj +++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj @@ -41,6 +41,10 @@ False ..\packages\NLog.2.1.0\lib\net45\NLog.dll + + False + ..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll + ..\packages\sharpcompress.0.10.1.3\lib\net40\SharpCompress.dll @@ -55,9 +59,6 @@ - - ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll - diff --git a/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs b/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs index 4a6b9255c1..3ff9560405 100644 --- a/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs +++ b/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs @@ -169,6 +169,8 @@ namespace MediaBrowser.Common.Implementations.Serialization ServiceStack.Text.JsConfig.DateHandler = ServiceStack.Text.JsonDateHandler.ISO8601; ServiceStack.Text.JsConfig.ExcludeTypeInfo = true; ServiceStack.Text.JsConfig.IncludeNullValues = false; + ServiceStack.Text.JsConfig.AlwaysUseUtc = true; + ServiceStack.Text.JsConfig.AssumeUtc = true; } /// diff --git a/MediaBrowser.Common.Implementations/packages.config b/MediaBrowser.Common.Implementations/packages.config index f2fe488309..269ac0e561 100644 --- a/MediaBrowser.Common.Implementations/packages.config +++ b/MediaBrowser.Common.Implementations/packages.config @@ -1,7 +1,7 @@  - + \ No newline at end of file diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index f4d759a4d1..a9499dedda 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -35,18 +35,21 @@ 4 - - - - - ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll + + False + ..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Common.dll - - ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll + + False + ..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll - - ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll + + False + ..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll + + + diff --git a/MediaBrowser.Common/packages.config b/MediaBrowser.Common/packages.config index 6969b43c54..7411e313cd 100644 --- a/MediaBrowser.Common/packages.config +++ b/MediaBrowser.Common/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file diff --git a/MediaBrowser.Model/Querying/EpisodeQuery.cs b/MediaBrowser.Model/Querying/EpisodeQuery.cs index 56c50da7f7..589b46433a 100644 --- a/MediaBrowser.Model/Querying/EpisodeQuery.cs +++ b/MediaBrowser.Model/Querying/EpisodeQuery.cs @@ -5,6 +5,8 @@ namespace MediaBrowser.Model.Querying { public string UserId { get; set; } + public string SeasonId { get; set; } + public string SeriesId { get; set; } public bool? IsMissing { get; set; } diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index 9491139dbc..6a17ad133a 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -92,6 +92,18 @@ namespace MediaBrowser.Model.System /// The program data path. public string ProgramDataPath { get; set; } + /// + /// Gets or sets the items by name path. + /// + /// The items by name path. + public string ItemsByNamePath { get; set; } + + /// + /// Gets or sets the log path. + /// + /// The log path. + public string LogPath { get; set; } + /// /// Gets or sets the HTTP server port number. /// diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index f5ade2516c..2d32811d33 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -41,8 +41,29 @@ False ..\packages\MediaBrowser.BdInfo.1.0.0.5\lib\net20\BDInfo.dll - - ..\packages\ServiceStack.OrmLite.Sqlite32.3.9.63\lib\net40\ServiceStack.OrmLite.SqliteNET.dll + + False + ..\packages\ServiceStack.3.9.70\lib\net35\ServiceStack.dll + + + False + ..\packages\ServiceStack.Api.Swagger.3.9.70\lib\net35\ServiceStack.Api.Swagger.dll + + + False + ..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Common.dll + + + False + ..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll + + + False + ..\packages\ServiceStack.3.9.70\lib\net35\ServiceStack.ServiceInterface.dll + + + False + ..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll @@ -72,39 +93,12 @@ ..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll - - ..\packages\ServiceStack.3.9.62\lib\net35\ServiceStack.dll - - - ..\packages\ServiceStack.Api.Swagger.3.9.59\lib\net35\ServiceStack.Api.Swagger.dll - - - ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll - - - ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll - ..\packages\ServiceStack.OrmLite.SqlServer.3.9.43\lib\ServiceStack.OrmLite.SqlServer.dll ..\packages\ServiceStack.Redis.3.9.43\lib\net35\ServiceStack.Redis.dll - - ..\packages\ServiceStack.3.9.62\lib\net35\ServiceStack.ServiceInterface.dll - - - ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll - - - ..\packages\ServiceStack.OrmLite.Sqlite.Mono.3.9.64\lib\net35\Mono.Data.Sqlite.dll - - - ..\packages\ServiceStack.OrmLite.Sqlite.Mono.3.9.64\lib\net35\ServiceStack.OrmLite.dll - - - ..\packages\ServiceStack.OrmLite.Sqlite.Mono.3.9.64\lib\net35\ServiceStack.OrmLite.Sqlite.dll - @@ -268,7 +262,6 @@ - PreserveNewest @@ -287,9 +280,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -323,6 +313,10 @@ PreserveNewest + + + PreserveNewest + diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index eeeedfe362..d5abe58caf 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -6,13 +6,11 @@ - - - - - + + + - + \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/swagger-ui/index.html b/MediaBrowser.Server.Implementations/swagger-ui/index.html index 0fcc069596..49f983a723 100644 --- a/MediaBrowser.Server.Implementations/swagger-ui/index.html +++ b/MediaBrowser.Server.Implementations/swagger-ui/index.html @@ -20,7 +20,7 @@ $(function () { window.swaggerUi = new SwaggerUi({ discoveryUrl: "../resources", - apiKey:"special-key", + apiKey: "special-key", dom_id:"swagger-ui-container", supportHeaderParams: false, supportedSubmitMethods: ['get', 'post', 'put'], diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index be865881af..d2d700839e 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -609,6 +609,8 @@ namespace MediaBrowser.ServerApplication CompletedInstallations = InstallationManager.CompletedInstallations.ToList(), Id = _systemId, ProgramDataPath = ApplicationPaths.ProgramDataPath, + LogPath = ApplicationPaths.LogDirectoryPath, + ItemsByNamePath = ApplicationPaths.ItemsByNamePath, MacAddress = GetMacAddress(), HttpServerPortNumber = ServerConfigurationManager.Configuration.HttpServerPortNumber, OperatingSystem = Environment.OSVersion.ToString(), diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 795799ca3e..5f05bc7875 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -131,17 +131,17 @@ False ..\packages\MediaBrowser.IsoMounting.3.0.65\lib\net45\pfmclrapi.dll - + False - ..\packages\ServiceStack.3.9.62\lib\net35\ServiceStack.dll + ..\packages\ServiceStack.3.9.70\lib\net35\ServiceStack.dll - + False - ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll + ..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Common.dll - + False - ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll + ..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll ..\packages\ServiceStack.OrmLite.SqlServer.3.9.44\lib\ServiceStack.OrmLite.SqlServer.dll @@ -149,13 +149,13 @@ ..\packages\ServiceStack.Redis.3.9.44\lib\net35\ServiceStack.Redis.dll - + False - ..\packages\ServiceStack.3.9.62\lib\net35\ServiceStack.ServiceInterface.dll + ..\packages\ServiceStack.3.9.70\lib\net35\ServiceStack.ServiceInterface.dll - + False - ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll + ..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll False diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config index e01ca1f672..5d7c3265f3 100644 --- a/MediaBrowser.ServerApplication/packages.config +++ b/MediaBrowser.ServerApplication/packages.config @@ -3,10 +3,10 @@ - - + + - + \ No newline at end of file diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 73b281d9a4..d36413fc2b 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -37,21 +37,24 @@ Always + + False + ..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Common.dll + + + False + ..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll + + + False + ..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll + - - ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll - - - ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll - - - ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll - diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config index a839ece186..4fa4e5a9ec 100644 --- a/MediaBrowser.WebDashboard/packages.config +++ b/MediaBrowser.WebDashboard/packages.config @@ -1,6 +1,6 @@  - - + + \ No newline at end of file diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 17311530fa..150b081dc4 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.254 + 3.0.255 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index e95a638ab8..c28fa7922b 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.254 + 3.0.255 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 38bb37530e..32f1da5365 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.254 + 3.0.255 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - + From 04bc137fb30b82e699bc8ea5d84479f01ad6c6ca Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 1 Dec 2013 01:25:05 -0500 Subject: [PATCH 46/67] make dontfetchmeta changes recursive --- MediaBrowser.Api/ItemUpdateService.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 95876f1a5b..48d292bbc2 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -105,13 +105,27 @@ namespace MediaBrowser.Api Task.WaitAll(task); } - private Task UpdateItem(UpdateItem request) + private async Task UpdateItem(UpdateItem request) { var item = _dtoService.GetItemByDtoId(request.ItemId); + var newEnableInternetProviders = request.EnableInternetProviders ?? true; + var dontFetchMetaChanged = item.DontFetchMeta != !newEnableInternetProviders; + UpdateItem(request, item); - return _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None); + await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); + + if (dontFetchMetaChanged && item.IsFolder) + { + var folder = (Folder)item; + + foreach (var child in folder.RecursiveChildren.ToList()) + { + child.DontFetchMeta = !newEnableInternetProviders; + await _libraryManager.UpdateItem(child, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); + } + } } public void Post(UpdatePerson request) From ccd51222e65cbc6faa409e4269bc5795bfd0f0ae Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 1 Dec 2013 01:25:19 -0500 Subject: [PATCH 47/67] updated live tv endpoints --- .../LiveTv/ILiveTvService.cs | 22 +++++- .../LiveTv/RecurringTimerInfo.cs | 73 ------------------- MediaBrowser.Controller/LiveTv/TimerInfo.cs | 6 +- .../MediaBrowser.Controller.csproj | 2 +- MediaBrowser.Model/LiveTv/TimerInfoDto.cs | 6 +- .../Library/ResolverHelper.cs | 5 +- .../LiveTv/LiveTvManager.cs | 2 +- .../Api/DashboardService.cs | 1 + .../MediaBrowser.WebDashboard.csproj | 7 +- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- 12 files changed, 42 insertions(+), 92 deletions(-) delete mode 100644 MediaBrowser.Controller/LiveTv/RecurringTimerInfo.cs diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs index 2b58d8bc56..1627ad400f 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs @@ -47,6 +47,22 @@ namespace MediaBrowser.Controller.LiveTv /// Task. Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken); + /// + /// Creates the series timer asynchronous. + /// + /// The information. + /// The cancellation token. + /// Task. + Task CreateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken); + + /// + /// Updates the series timer asynchronous. + /// + /// The information. + /// The cancellation token. + /// Task. + Task UpdateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken); + /// /// Gets the channel image asynchronous. /// @@ -70,11 +86,11 @@ namespace MediaBrowser.Controller.LiveTv Task> GetTimersAsync(CancellationToken cancellationToken); /// - /// Gets the recurring timers asynchronous. + /// Gets the series timers asynchronous. /// /// The cancellation token. - /// Task{IEnumerable{RecurringTimerInfo}}. - Task> GetRecurringTimersAsync(CancellationToken cancellationToken); + /// Task{IEnumerable{SeriesTimerInfo}}. + Task> GetSeriesTimersAsync(CancellationToken cancellationToken); /// /// Gets the programs asynchronous. diff --git a/MediaBrowser.Controller/LiveTv/RecurringTimerInfo.cs b/MediaBrowser.Controller/LiveTv/RecurringTimerInfo.cs deleted file mode 100644 index 08a8b1f92f..0000000000 --- a/MediaBrowser.Controller/LiveTv/RecurringTimerInfo.cs +++ /dev/null @@ -1,73 +0,0 @@ -using MediaBrowser.Model.LiveTv; -using System; - -namespace MediaBrowser.Controller.LiveTv -{ - public class RecurringTimerInfo - { - /// - /// Id of the recording. - /// - public string Id { get; set; } - - /// - /// ChannelId of the recording. - /// - public string ChannelId { get; set; } - - /// - /// ChannelName of the recording. - /// - public string ChannelName { get; set; } - - /// - /// Gets or sets the program identifier. - /// - /// The program identifier. - public string ProgramId { get; set; } - - /// - /// Name of the recording. - /// - public string Name { get; set; } - - /// - /// Description of the recording. - /// - public string Description { get; set; } - - /// - /// The start date of the recording, in UTC. - /// - public DateTime StartDate { get; set; } - - /// - /// The end date of the recording, in UTC. - /// - public DateTime EndDate { get; set; } - - /// - /// Gets or sets the status. - /// - /// The status. - public RecordingStatus Status { get; set; } - - /// - /// Gets or sets the pre padding seconds. - /// - /// The pre padding seconds. - public int PrePaddingSeconds { get; set; } - - /// - /// Gets or sets the post padding seconds. - /// - /// The post padding seconds. - public int PostPaddingSeconds { get; set; } - - /// - /// Gets or sets the type of the recurrence. - /// - /// The type of the recurrence. - public RecurrenceType RecurrenceType { get; set; } - } -} diff --git a/MediaBrowser.Controller/LiveTv/TimerInfo.cs b/MediaBrowser.Controller/LiveTv/TimerInfo.cs index 0fe7c34f2d..3df0b8ccab 100644 --- a/MediaBrowser.Controller/LiveTv/TimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/TimerInfo.cs @@ -11,10 +11,10 @@ namespace MediaBrowser.Controller.LiveTv public string Id { get; set; } /// - /// Gets or sets the recurring timer identifier. + /// Gets or sets the series timer identifier. /// - /// The recurring timer identifier. - public string RecurringTimerId { get; set; } + /// The series timer identifier. + public string SeriesTimerId { get; set; } /// /// ChannelId of the recording. diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index ec0439c2c7..a309d815c3 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -110,7 +110,7 @@ - + diff --git a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs index f7d63e9687..59a320bfda 100644 --- a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs +++ b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs @@ -58,10 +58,10 @@ namespace MediaBrowser.Model.LiveTv public RecordingStatus Status { get; set; } /// - /// Gets or sets the recurring timer identifier. + /// Gets or sets the series timer identifier. /// - /// The recurring timer identifier. - public string RecurringTimerId { get; set; } + /// The series timer identifier. + public string SeriesTimerId { get; set; } /// /// Gets or sets the pre padding seconds. diff --git a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs index 620bcaee4a..e32fcd627b 100644 --- a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs +++ b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs @@ -1,11 +1,11 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Resolvers; using System; using System.IO; +using System.Linq; using System.Text.RegularExpressions; namespace MediaBrowser.Server.Implementations.Library @@ -48,7 +48,8 @@ namespace MediaBrowser.Server.Implementations.Library // Make sure the item has a name EnsureName(item); - item.DontFetchMeta = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1; + item.DontFetchMeta = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 || + item.Parents.Any(i => i.DontFetchMeta); // Make sure DateCreated and DateModified have values EntityResolutionHelper.EnsureDates(fileSystem, item, args, true); diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index ea8ea45b69..261c915cb2 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -514,7 +514,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv ExternalId = info.Id, ChannelId = GetInternalChannelId(service.Name, info.ChannelId, info.ChannelName).ToString("N"), Status = info.Status, - RecurringTimerId = info.RecurringTimerId, + SeriesTimerId = info.SeriesTimerId, PrePaddingSeconds = info.PrePaddingSeconds, PostPaddingSeconds = info.PostPaddingSeconds }; diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 9076777a5b..17508b2bdc 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -482,6 +482,7 @@ namespace MediaBrowser.WebDashboard.Api "livetvchannel.js", "livetvchannels.js", "livetvguide.js", + "livetvrecording.js", "livetvrecordings.js", "livetvtimer.js", "livetvtimers.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index d36413fc2b..e9a70ad973 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -92,6 +92,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -353,6 +356,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -1214,7 +1220,6 @@ - diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 150b081dc4..946ec617bd 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.255 + 3.0.256 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index c28fa7922b..04ac3559c0 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.255 + 3.0.256 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 32f1da5365..d813a00ace 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.255 + 3.0.256 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - + From 037272ac6160849b3fdc00a53dd99b367fabb8ce Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 1 Dec 2013 11:08:52 -0500 Subject: [PATCH 48/67] fixing missing check-in --- MediaBrowser.Controller/Entities/BaseItem.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index a10a2c263d..3f06136434 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1,7 +1,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; From 415a2d4e314baf3356f94dbea7e7cfaf2b4880dd Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 1 Dec 2013 11:09:05 -0500 Subject: [PATCH 49/67] add missing files --- .../LiveTv/SeriesTimerInfo.cs | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs diff --git a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs new file mode 100644 index 0000000000..178fcff82e --- /dev/null +++ b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs @@ -0,0 +1,79 @@ +using MediaBrowser.Model.LiveTv; +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Controller.LiveTv +{ + public class SeriesTimerInfo + { + /// + /// Id of the recording. + /// + public string Id { get; set; } + + /// + /// ChannelId of the recording. + /// + public string ChannelId { get; set; } + + /// + /// ChannelName of the recording. + /// + public string ChannelName { get; set; } + + /// + /// Gets or sets the program identifier. + /// + /// The program identifier. + public string ProgramId { get; set; } + + /// + /// Name of the recording. + /// + public string Name { get; set; } + + /// + /// Description of the recording. + /// + public string Description { get; set; } + + /// + /// The start date of the recording, in UTC. + /// + public DateTime StartDate { get; set; } + + /// + /// The end date of the recording, in UTC. + /// + public DateTime EndDate { get; set; } + + /// + /// Gets or sets the pre padding seconds. + /// + /// The pre padding seconds. + public int PrePaddingSeconds { get; set; } + + /// + /// Gets or sets the post padding seconds. + /// + /// The post padding seconds. + public int PostPaddingSeconds { get; set; } + + /// + /// Gets or sets the type of the recurrence. + /// + /// The type of the recurrence. + public RecurrenceType RecurrenceType { get; set; } + + /// + /// Gets or sets the days. + /// + /// The days. + public List Days { get; set; } + + public SeriesTimerInfo() + { + Days = new List(); + } + } +} From b827f4058da428e5688b249ba1e7fff3c91f83a7 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 1 Dec 2013 13:57:52 -0500 Subject: [PATCH 50/67] improve movie matching by looking for titles with same year first --- .../Movies/MovieDbProvider.cs | 79 +++++++++++++------ 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index b4903ba69f..fe409c0900 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -441,36 +441,71 @@ namespace MediaBrowser.Providers.Movies if (searchResult != null) { - foreach (var possible in searchResult.results) - { - string matchedName = possible.title ?? possible.name; - string id = possible.id.ToString(CultureInfo.InvariantCulture); + return FindIdOfBestResult(searchResult.results, name, year); + } + + return null; + } - if (matchedName != null) + private string FindIdOfBestResult(List results, string name, int? year) + { + if (year.HasValue) + { + // Take the first result from the same year + var id = results.Where(i => + { + // Make sure it has a name + if (!string.IsNullOrEmpty(i.title ?? i.name)) { - Logger.Debug("Match " + matchedName + " for " + name); - if (year != null) + DateTime r; + + // These dates are always in this exact format + if (DateTime.TryParseExact(i.release_date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out r)) { - DateTime r; - - //These dates are always in this exact format - if (DateTime.TryParseExact(possible.release_date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out r)) - { - if (Math.Abs(r.Year - year.Value) > 1) // allow a 1 year tolerance on release date - { - Logger.Debug("Result " + matchedName + " released on " + r + " did not match year " + year); - continue; - } - } + return r.Year == year.Value; } - //matched name and year - return id; } + return false; + }) + .Select(i => i.id.ToString(CultureInfo.InvariantCulture)) + .FirstOrDefault(); + + if (!string.IsNullOrEmpty(id)) + { + return id; + } + + // Take the first result within one year + id = results.Where(i => + { + // Make sure it has a name + if (!string.IsNullOrEmpty(i.title ?? i.name)) + { + DateTime r; + + // These dates are always in this exact format + if (DateTime.TryParseExact(i.release_date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out r)) + { + return Math.Abs(r.Year - year.Value) <= 1; + } + } + + return false; + }) + .Select(i => i.id.ToString(CultureInfo.InvariantCulture)) + .FirstOrDefault(); + + if (!string.IsNullOrEmpty(id)) + { + return id; } } - return null; + // Just take the first one + return results.Where(i => !string.IsNullOrEmpty(i.title ?? i.name)) + .Select(i => i.id.ToString(CultureInfo.InvariantCulture)) + .FirstOrDefault(); } /// @@ -601,7 +636,7 @@ namespace MediaBrowser.Providers.Movies return path; } - + internal string GetImagesDataFilePath(BaseItem item) { var path = GetDataFilePath(item); From e191836ea0405c5a152a742fc5526d8ceb5c5db5 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 1 Dec 2013 13:58:06 -0500 Subject: [PATCH 51/67] fix tmdbid override for movies --- .../Library/Resolvers/Movies/MovieResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 07beabb832..03e29dd38f 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -172,7 +172,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies private void SetProviderIdFromPath(Video item) { //we need to only look at the name of this actual item (not parents) - var justName = Path.GetFileName(item.Path); + var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path) : Path.GetFileName(Path.GetDirectoryName(item.Path)); var id = justName.GetAttributeValue("tmdbid"); From 7ac2f7481734694ae411f5481c6a73264671b55b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 1 Dec 2013 14:31:58 -0500 Subject: [PATCH 52/67] fixes #629 - Deleting a movie only deletes the video file --- MediaBrowser.Api/LibraryService.cs | 15 +++++++++------ MediaBrowser.Controller/Entities/BaseItem.cs | 9 +++++++++ MediaBrowser.Controller/Entities/Game.cs | 12 +++++++++++- MediaBrowser.Controller/Entities/TV/Episode.cs | 5 +++++ MediaBrowser.Controller/Entities/Video.cs | 9 +++++++++ 5 files changed, 43 insertions(+), 7 deletions(-) diff --git a/MediaBrowser.Api/LibraryService.cs b/MediaBrowser.Api/LibraryService.cs index 55ee454d2e..cf62e42ba7 100644 --- a/MediaBrowser.Api/LibraryService.cs +++ b/MediaBrowser.Api/LibraryService.cs @@ -465,13 +465,16 @@ namespace MediaBrowser.Api if (item.LocationType == LocationType.FileSystem) { - if (Directory.Exists(item.Path)) + foreach (var path in item.GetDeletePaths().ToList()) { - Directory.Delete(item.Path, true); - } - else if (File.Exists(item.Path)) - { - File.Delete(item.Path); + if (Directory.Exists(path)) + { + Directory.Delete(path, true); + } + else if (File.Exists(path)) + { + File.Delete(path); + } } if (parent != null) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 3f06136434..d882824293 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1765,5 +1765,14 @@ namespace MediaBrowser.Controller.Entities // See if we can avoid a file system lookup by looking for the file in ResolveArgs return metaFileEntry == null ? FileSystem.GetLastWriteTimeUtc(imagePath) : FileSystem.GetLastWriteTimeUtc(metaFileEntry); } + + /// + /// Gets the file system path to delete when the item is to be deleted + /// + /// + public virtual IEnumerable GetDeletePaths() + { + return new[] { Path }; + } } } diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs index e8374c2743..ea39cf50ad 100644 --- a/MediaBrowser.Controller/Entities/Game.cs +++ b/MediaBrowser.Controller/Entities/Game.cs @@ -7,7 +7,7 @@ namespace MediaBrowser.Controller.Entities public class Game : BaseItem, IHasSoundtracks { public List SoundtrackIds { get; set; } - + public Game() { MultiPartGameFiles = new List(); @@ -84,5 +84,15 @@ namespace MediaBrowser.Controller.Entities } return base.GetUserDataKey(); } + + public override IEnumerable GetDeletePaths() + { + if (!IsInMixedFolder) + { + return new[] { System.IO.Path.GetDirectoryName(Path) }; + } + + return base.GetDeletePaths(); + } } } diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index f090ce2a27..5eab1cae0c 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -248,5 +248,10 @@ namespace MediaBrowser.Controller.Entities.TV { get { return LocationType == Model.Entities.LocationType.Virtual && IsUnaired; } } + + public override IEnumerable GetDeletePaths() + { + return new[] { Path }; + } } } diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 6a27ed6906..ef768f6280 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -258,5 +258,14 @@ namespace MediaBrowser.Controller.Entities }).ToList(); } + public override IEnumerable GetDeletePaths() + { + if (!IsInMixedFolder) + { + return new[] { System.IO.Path.GetDirectoryName(Path) }; + } + + return base.GetDeletePaths(); + } } } From 80a56ddcfaf31655beb5e83d1fc904295b814c33 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 1 Dec 2013 15:17:24 -0500 Subject: [PATCH 53/67] fixes #635 - FFmpeg not being stopped in safari --- MediaBrowser.WebDashboard/ApiClient.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js index de96c4de94..16e2ae3b96 100644 --- a/MediaBrowser.WebDashboard/ApiClient.js +++ b/MediaBrowser.WebDashboard/ApiClient.js @@ -1036,7 +1036,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi self.getScheduledTasks = function (options) { options = options || {}; - + var url = self.getUrl("ScheduledTasks", options); return self.ajax({ @@ -1402,6 +1402,19 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi }); }; + self.stopActiveEncodings = function () { + + var url = self.getUrl("Videos/ActiveEncodings", { + + deviceId: deviceId + }); + + return self.ajax({ + type: "DELETE", + url: url + }); + }; + self.updateItemImageIndex = function (itemId, itemType, itemName, imageType, imageIndex, newIndex) { if (!imageType) { From 42a2522637d1772381eecee0d32ed3ef60fa3c73 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 1 Dec 2013 15:17:40 -0500 Subject: [PATCH 54/67] read/write remote trailers to xml --- .../Playback/BaseStreamingService.cs | 2 +- .../Providers/BaseItemXmlParser.cs | 39 +++++++++++++++ .../Savers/XmlSaverHelpers.cs | 12 ++++- .../Api/DashboardService.cs | 2 +- .../MediaBrowser.WebDashboard.csproj | 48 +++++++++---------- MediaBrowser.WebDashboard/packages.config | 2 +- 6 files changed, 76 insertions(+), 29 deletions(-) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 67d9cf7a47..9ad0703ae6 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -695,7 +695,7 @@ namespace MediaBrowser.Api.Playback // This is arbitrary, but add a little buffer time when internet streaming if (state.Item.LocationType == LocationType.Remote) { - await Task.Delay(2000).ConfigureAwait(false); + await Task.Delay(4000).ConfigureAwait(false); } } diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index 8ee622eda9..14c83bed43 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -68,6 +68,7 @@ namespace MediaBrowser.Controller.Providers item.Genres.Clear(); item.People.Clear(); item.Tags.Clear(); + item.RemoteTrailers.Clear(); //Fetch(item, metadataFile, settings, Encoding.GetEncoding("ISO-8859-1"), cancellationToken); Fetch(item, metadataFile, settings, Encoding.UTF8, cancellationToken); @@ -482,6 +483,15 @@ namespace MediaBrowser.Controller.Providers break; } + case "Trailers": + { + using (var subtree = reader.ReadSubtree()) + { + FetchDataFromTrailersNode(subtree, item); + } + break; + } + case "ReleaseYear": case "ProductionYear": { @@ -922,6 +932,35 @@ namespace MediaBrowser.Controller.Providers } } + private void FetchDataFromTrailersNode(XmlReader reader, T item) + { + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "Trailer": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + item.AddTrailerUrl(val, false); + } + break; + } + + default: + reader.Skip(); + break; + } + } + } + } + protected async Task FetchChaptersFromXmlNode(BaseItem item, XmlReader reader, IItemRepository repository, CancellationToken cancellationToken) { var runtime = item.RunTimeTicks ?? 0; diff --git a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs index a0c830db4a..5a49e4f36b 100644 --- a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs +++ b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs @@ -63,6 +63,7 @@ namespace MediaBrowser.Providers.Savers "TMDbCollectionId", "TMDbId", "Trailer", + "Trailers", "TVcomId", "TvDbId", "Type", @@ -177,7 +178,7 @@ namespace MediaBrowser.Providers.Savers } } } - + return builder.ToString(); } @@ -269,7 +270,14 @@ namespace MediaBrowser.Providers.Savers if (item.RemoteTrailers.Count > 0) { - builder.Append("" + SecurityElement.Escape(item.RemoteTrailers[0].Url) + ""); + builder.Append(""); + + foreach (var trailer in item.RemoteTrailers) + { + builder.Append("" + SecurityElement.Escape(trailer.Url) + ""); + } + + builder.Append(""); } if (item.Budget.HasValue) diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 17508b2bdc..fc89441aef 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -430,7 +430,7 @@ namespace MediaBrowser.WebDashboard.Api "http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js", "http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js", "scripts/all.js" + versionString, - "thirdparty/jstree1.0fix3/jquery.jstree.js" + "thirdparty/jstree1.0/jquery.jstree.js" }; var tags = files.Select(s => string.Format("", s)).ToArray(); diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index e9a70ad973..3b2714b2d9 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -610,76 +610,76 @@ PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config index 4fa4e5a9ec..f13a431e3a 100644 --- a/MediaBrowser.WebDashboard/packages.config +++ b/MediaBrowser.WebDashboard/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file From ad52d8b5d96bea85c17f37da7ff3334164f8d4a4 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 1 Dec 2013 21:24:14 -0500 Subject: [PATCH 55/67] fixes #640 - Add management filters --- MediaBrowser.Api/UserLibrary/ItemsService.cs | 100 ++++++++++++++++++ MediaBrowser.Controller/Entities/BaseItem.cs | 1 + MediaBrowser.Controller/Entities/Video.cs | 5 +- .../MediaBrowser.Controller.csproj | 1 + .../Providers/NameParser.cs | 39 +++++++ MediaBrowser.Mono.userprefs | 21 ++-- .../Movies/MovieDbProvider.cs | 33 +----- .../Movies/OpenMovieDatabaseProvider.cs | 12 +-- ...MediaBrowser.Server.Implementations.csproj | 11 ++ .../packages.config | 1 + .../MediaBrowser.Server.Mono.csproj | 7 +- .../Native/HttpClientFactory.cs | 24 ----- .../Providers/MovieDbProviderTests.cs | 13 +-- 13 files changed, 183 insertions(+), 85 deletions(-) create mode 100644 MediaBrowser.Controller/Providers/NameParser.cs delete mode 100644 MediaBrowser.Server.Mono/Native/HttpClientFactory.cs diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index cae74cc2f4..d6f3488361 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -1,4 +1,5 @@ using System.Globalization; +using System.IO; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -6,6 +7,7 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; +using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using ServiceStack.ServiceHost; @@ -212,6 +214,21 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "MaxPremiereDate", Description = "Optional. The maximum premiere date. Format = yyyyMMddHHmmss", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] public string MaxPremiereDate { get; set; } + + [ApiMember(Name = "HasOverview", Description = "Optional filter by items that have an overview or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool? HasOverview { get; set; } + + [ApiMember(Name = "HasImdbId", Description = "Optional filter by items that have an imdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool? HasImdbId { get; set; } + + [ApiMember(Name = "HasTmdbId", Description = "Optional filter by items that have a tmdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool? HasTmdbId { get; set; } + + [ApiMember(Name = "HasTvdbId", Description = "Optional filter by items that have a tvdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool? HasTvdbId { get; set; } + + [ApiMember(Name = "IsYearMismatched", Description = "Optional filter by items that are potentially misidentified.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool? IsYearMismatched { get; set; } } /// @@ -1029,9 +1046,92 @@ namespace MediaBrowser.Api.UserLibrary items = items.Where(i => i.PremiereDate.HasValue && i.PremiereDate.Value <= date); } + if (request.HasOverview.HasValue) + { + var filterValue = request.HasOverview.Value; + + items = items.Where(i => + { + var hasValue = !string.IsNullOrEmpty(i.Overview); + + return hasValue == filterValue; + }); + } + + if (request.HasImdbId.HasValue) + { + var filterValue = request.HasImdbId.Value; + + items = items.Where(i => + { + var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Imdb)); + + return hasValue == filterValue; + }); + } + + if (request.HasTmdbId.HasValue) + { + var filterValue = request.HasTmdbId.Value; + + items = items.Where(i => + { + var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tmdb)); + + return hasValue == filterValue; + }); + } + + if (request.HasTvdbId.HasValue) + { + var filterValue = request.HasTvdbId.Value; + + items = items.Where(i => + { + var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb)); + + return hasValue == filterValue; + }); + } + + if (request.IsYearMismatched.HasValue) + { + var filterValue = request.IsYearMismatched.Value; + + items = items.Where(i => IsYearMismatched(i) == filterValue); + } + return items; } + private bool IsYearMismatched(BaseItem item) + { + if (item.ProductionYear.HasValue) + { + var path = item.Path; + + if (!string.IsNullOrEmpty(path)) + { + int? yearInName; + string name; + NameParser.ParseName(Path.GetFileName(path), out name, out yearInName); + + // Go up a level if we didn't get a year + if (!yearInName.HasValue) + { + NameParser.ParseName(Path.GetFileName(Path.GetDirectoryName(path)), out name, out yearInName); + } + + if (yearInName.HasValue) + { + return yearInName.Value != item.ProductionYear.Value; + } + } + } + + return false; + } + /// /// Determines whether the specified item has image. /// diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index d882824293..25da18fcac 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -138,6 +138,7 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the type of the location. /// /// The type of the location. + [IgnoreDataMember] public virtual LocationType LocationType { get diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index ef768f6280..9b02571b00 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -262,7 +262,10 @@ namespace MediaBrowser.Controller.Entities { if (!IsInMixedFolder) { - return new[] { System.IO.Path.GetDirectoryName(Path) }; + if (VideoType == VideoType.VideoFile || VideoType == VideoType.Iso) + { + return new[] { System.IO.Path.GetDirectoryName(Path) }; + } } return base.GetDeletePaths(); diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index a309d815c3..9b89b12c50 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -117,6 +117,7 @@ + diff --git a/MediaBrowser.Controller/Providers/NameParser.cs b/MediaBrowser.Controller/Providers/NameParser.cs new file mode 100644 index 0000000000..726f0e60e3 --- /dev/null +++ b/MediaBrowser.Controller/Providers/NameParser.cs @@ -0,0 +1,39 @@ +using System; +using System.Text.RegularExpressions; + +namespace MediaBrowser.Controller.Providers +{ + public static class NameParser + { + static readonly Regex[] NameMatches = new[] { + new Regex(@"(?.*)\((?\d{4})\)"), // matches "My Movie (2001)" and gives us the name and the year + new Regex(@"(?.*)(\.(?\d{4})(\.|$)).*$"), + new Regex(@"(?.*)") // last resort matches the whole string as the name + }; + + + /// + /// Parses the name. + /// + /// The name. + /// Name of the just. + /// The year. + public static void ParseName(string name, out string justName, out int? year) + { + justName = null; + year = null; + foreach (var re in NameMatches) + { + Match m = re.Match(name); + if (m.Success) + { + justName = m.Groups["name"].Value.Trim(); + string y = m.Groups["year"] != null ? m.Groups["year"].Value : null; + int temp; + year = Int32.TryParse(y, out temp) ? temp : (int?)null; + break; + } + } + } + } +} diff --git a/MediaBrowser.Mono.userprefs b/MediaBrowser.Mono.userprefs index f1260b1dad..4a91e2e60f 100644 --- a/MediaBrowser.Mono.userprefs +++ b/MediaBrowser.Mono.userprefs @@ -1,10 +1,14 @@  - + - - - + + + + + + + @@ -16,10 +20,13 @@ - + + + + + - - + diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index fe409c0900..e4fe2a914d 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -15,7 +15,6 @@ using System.Globalization; using System.IO; using System.Linq; using System.Net; -using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -190,12 +189,6 @@ namespace MediaBrowser.Providers.Movies internal static string ApiKey = "f6bd687ffa63cd282b6ff2c6877f2669"; internal static string AcceptHeader = "application/json,image/*"; - static readonly Regex[] NameMatches = new[] { - new Regex(@"(?.*)\((?\d{4})\)"), // matches "My Movie (2001)" and gives us the name and the year - new Regex(@"(?.*)(\.(?\d{4})(\.|$)).*$"), - new Regex(@"(?.*)") // last resort matches the whole string as the name - }; - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) { if (string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Tmdb))) @@ -309,30 +302,6 @@ namespace MediaBrowser.Providers.Movies return false; } - /// - /// Parses the name. - /// - /// The name. - /// Name of the just. - /// The year. - public static void ParseName(string name, out string justName, out int? year) - { - justName = null; - year = null; - foreach (var re in NameMatches) - { - Match m = re.Match(name); - if (m.Success) - { - justName = m.Groups["name"].Value.Trim(); - string y = m.Groups["year"] != null ? m.Groups["year"].Value : null; - int temp; - year = Int32.TryParse(y, out temp) ? temp : (int?)null; - break; - } - } - } - /// /// Finds the id. /// @@ -343,7 +312,7 @@ namespace MediaBrowser.Providers.Movies { int? yearInName; string name = item.Name; - ParseName(name, out name, out yearInName); + NameParser.ParseName(name, out name, out yearInName); var year = item.ProductionYear ?? yearInName; diff --git a/MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs b/MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs index 6550396e51..d881859c6e 100644 --- a/MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs +++ b/MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs @@ -109,19 +109,11 @@ namespace MediaBrowser.Providers.Movies public override async Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) { - BaseProviderInfo data; - - if (!item.ProviderData.TryGetValue(Id, out data)) - { - data = new BaseProviderInfo(); - item.ProviderData[Id] = data; - } - var imdbId = item.GetProviderId(MetadataProviders.Imdb); if (string.IsNullOrEmpty(imdbId)) { - data.LastRefreshStatus = ProviderRefreshStatus.Success; + SetLastRefreshed(item, DateTime.UtcNow); return true; } @@ -182,9 +174,7 @@ namespace MediaBrowser.Providers.Movies ParseAdditionalMetadata(item, result); } - data.LastRefreshStatus = ProviderRefreshStatus.Success; SetLastRefreshed(item, DateTime.UtcNow); - return true; } diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 2d32811d33..3bfbdea3ea 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -41,6 +41,9 @@ False ..\packages\MediaBrowser.BdInfo.1.0.0.5\lib\net20\BDInfo.dll + + ..\packages\ServiceStack.OrmLite.Sqlite.Mono.3.9.70\lib\net35\Mono.Data.Sqlite.dll + False ..\packages\ServiceStack.3.9.70\lib\net35\ServiceStack.dll @@ -57,6 +60,13 @@ False ..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll + + False + ..\packages\ServiceStack.OrmLite.Sqlite.Mono.3.9.70\lib\net35\ServiceStack.OrmLite.dll + + + ..\packages\ServiceStack.OrmLite.Sqlite.Mono.3.9.70\lib\net35\ServiceStack.OrmLite.Sqlite.dll + False ..\packages\ServiceStack.3.9.70\lib\net35\ServiceStack.ServiceInterface.dll @@ -262,6 +272,7 @@ + PreserveNewest diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index d5abe58caf..488dbc1ae8 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -9,6 +9,7 @@ + diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj index 900169c707..e32dad8d7b 100644 --- a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj +++ b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj @@ -44,13 +44,13 @@ + - ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll + ..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Common.dll - ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll + ..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll - @@ -82,7 +82,6 @@ - FFMpeg\FFMpegDownloader.cs diff --git a/MediaBrowser.Server.Mono/Native/HttpClientFactory.cs b/MediaBrowser.Server.Mono/Native/HttpClientFactory.cs deleted file mode 100644 index 0fceab0608..0000000000 --- a/MediaBrowser.Server.Mono/Native/HttpClientFactory.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Net.Http; - -namespace MediaBrowser.ServerApplication.Native -{ - /// - /// Class HttpClientFactory - /// - public static class HttpClientFactory - { - /// - /// Gets the HTTP client. - /// - /// if set to true [enable HTTP compression]. - /// HttpClient. - public static HttpClient GetHttpClient(bool enableHttpCompression) - { - return new HttpClient() - { - Timeout = TimeSpan.FromSeconds(20) - }; - } - } -} diff --git a/MediaBrowser.Tests/Providers/MovieDbProviderTests.cs b/MediaBrowser.Tests/Providers/MovieDbProviderTests.cs index f7a87c9d47..8f5dcc034a 100644 --- a/MediaBrowser.Tests/Providers/MovieDbProviderTests.cs +++ b/MediaBrowser.Tests/Providers/MovieDbProviderTests.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Providers.Movies; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Providers.Movies; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace MediaBrowser.Tests.Providers { @@ -8,27 +9,27 @@ namespace MediaBrowser.Tests.Providers { public void TestNameMatches() { var name = string.Empty; int? year = null; - MovieDbProvider.ParseName("My Movie (2013)", out name, out year); + NameParser.ParseName("My Movie (2013)", out name, out year); Assert.AreEqual("My Movie", name); Assert.AreEqual(2013, year); name = string.Empty; year = null; - MovieDbProvider.ParseName("My Movie 2 (2013)", out name, out year); + NameParser.ParseName("My Movie 2 (2013)", out name, out year); Assert.AreEqual("My Movie 2", name); Assert.AreEqual(2013, year); name = string.Empty; year = null; - MovieDbProvider.ParseName("My Movie 2001 (2013)", out name, out year); + NameParser.ParseName("My Movie 2001 (2013)", out name, out year); Assert.AreEqual("My Movie 2001", name); Assert.AreEqual(2013, year); name = string.Empty; year = null; - MovieDbProvider.ParseName("My Movie - 2 (2013)", out name, out year); + NameParser.ParseName("My Movie - 2 (2013)", out name, out year); Assert.AreEqual("My Movie - 2", name); Assert.AreEqual(2013, year); name = string.Empty; year = null; - MovieDbProvider.ParseName("curse.of.chucky.2013.stv.unrated.multi.1080p.bluray.x264-rough", out name, out year); + NameParser.ParseName("curse.of.chucky.2013.stv.unrated.multi.1080p.bluray.x264-rough", out name, out year); Assert.AreEqual("curse.of.chucky", name); Assert.AreEqual(2013, year); } From f5888aafa8058eb06e9e2c9dc31d85cbdc519734 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 1 Dec 2013 23:09:03 -0500 Subject: [PATCH 56/67] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 105f46e950..e64bb53fd6 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Beta: 3.0.5028.39800
![iOS](https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/apps/ios_1.jpg) ![iOS](https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/apps/ios_2.jpg) ![Media Browser Theater](https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/apps/mbt.png) -![Media Browser Theater](hhttps://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/apps/mbt1.png) +![Media Browser Theater](https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/apps/mbt1.png) ![Windows Phone](https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/apps/winphone.png) ![Roku](https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/apps/roku2.jpg) ![iOS](https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/apps/ios_3.jpg) From 81c23ed18322b3c90ecaa99b6f02378b8943adef Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 2 Dec 2013 10:47:24 -0500 Subject: [PATCH 57/67] have manual person image provider download data if not already present --- .../MediaBrowser.Providers.csproj | 1 - .../ManualMovieDbPersonImageProvider.cs | 16 ++- .../Music/AlbumDynamicInfoProvider.cs | 103 ------------------ .../Music/AlbumInfoFromSongProvider.cs | 37 ++++++- 4 files changed, 46 insertions(+), 111 deletions(-) delete mode 100644 MediaBrowser.Providers/Music/AlbumDynamicInfoProvider.cs diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index c7298b4622..b5b41c6d3d 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -89,7 +89,6 @@ - diff --git a/MediaBrowser.Providers/Movies/ManualMovieDbPersonImageProvider.cs b/MediaBrowser.Providers/Movies/ManualMovieDbPersonImageProvider.cs index ecfae9d5c0..b381de3322 100644 --- a/MediaBrowser.Providers/Movies/ManualMovieDbPersonImageProvider.cs +++ b/MediaBrowser.Providers/Movies/ManualMovieDbPersonImageProvider.cs @@ -46,7 +46,12 @@ namespace MediaBrowser.Providers.Movies return images.Where(i => i.Type == imageType); } - public async Task> GetAllImages(BaseItem item, CancellationToken cancellationToken) + public Task> GetAllImages(BaseItem item, CancellationToken cancellationToken) + { + return GetAllImagesInternal(item, true, cancellationToken); + } + + public async Task> GetAllImagesInternal(BaseItem item, bool retryOnMissingData, CancellationToken cancellationToken) { var id = item.GetProviderId(MetadataProviders.Tmdb); @@ -70,11 +75,18 @@ namespace MediaBrowser.Providers.Movies { } + + if (retryOnMissingData) + { + await MovieDbPersonProvider.Current.DownloadPersonInfo(id, cancellationToken).ConfigureAwait(false); + + return await GetAllImagesInternal(item, false, cancellationToken).ConfigureAwait(false); + } } return new List(); } - + private IEnumerable GetImages(MovieDbPersonProvider.Images images, string baseImageUrl) { var list = new List(); diff --git a/MediaBrowser.Providers/Music/AlbumDynamicInfoProvider.cs b/MediaBrowser.Providers/Music/AlbumDynamicInfoProvider.cs deleted file mode 100644 index 5c14a2f56c..0000000000 --- a/MediaBrowser.Providers/Music/AlbumDynamicInfoProvider.cs +++ /dev/null @@ -1,103 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Logging; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Providers.Music -{ - /// - /// Class MusicAlbumDynamicInfoProvider - /// - public class AlbumDynamicInfoProvider : BaseMetadataProvider, IDynamicInfoProvider - { - /// - /// Initializes a new instance of the class. - /// - /// The log manager. - /// The configuration manager. - public AlbumDynamicInfoProvider(ILogManager logManager, IServerConfigurationManager configurationManager) - : base(logManager, configurationManager) - { - } - - /// - /// Supportses the specified item. - /// - /// The item. - /// true if XXXX, false otherwise - public override bool Supports(BaseItem item) - { - return item is MusicAlbum; - } - - /// - /// Needses the refresh internal. - /// - /// The item. - /// The provider info. - /// true if XXXX, false otherwise - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - return true; - } - - /// - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// - /// The item. - /// if set to true [force]. - /// The cancellation token. - /// Task{System.Boolean}. - public override Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - var album = (MusicAlbum)item; - - var songs = album.RecursiveChildren - .OfType