From 1292eccfb57d82110d197d7fa2f1f6bbf01a7e18 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 20 Oct 2014 16:23:40 -0400 Subject: [PATCH 1/9] add reel --- MediaBrowser.Api/ItemUpdateService.cs | 2 +- MediaBrowser.Api/Movies/TrailersService.cs | 93 ++- MediaBrowser.Api/Playback/StreamState.cs | 7 +- MediaBrowser.Api/UserLibrary/ItemsService.cs | 4 +- .../Entities/Audio/MusicArtist.cs | 4 +- .../Entities/MusicVideo.cs | 37 +- MediaBrowser.Dlna/Didl/DidlBuilder.cs | 6 +- .../Parsers/MusicVideoXmlParser.cs | 18 +- .../Savers/MovieXmlSaver.cs | 4 +- .../ApiClient/IConnectionManager.cs | 4 +- .../Configuration/ServerConfiguration.cs | 2 + MediaBrowser.Model/Dto/BaseItemDto.cs | 3 +- .../Music/MusicVideoMetadataService.cs | 8 +- .../ServerConfigurationManager.cs | 4 - .../Dto/DtoService.cs | 27 +- .../Localization/Server/server.json | 7 +- .../Session/SessionManager.cs | 6 +- .../ApplicationHost.cs | 35 ++ .../Api/DashboardService.cs | 549 ++--------------- .../Api/PackageCreator.cs | 574 ++++++++++++++++++ .../MediaBrowser.WebDashboard.csproj | 1 + .../Parsers/MovieNfoParser.cs | 2 +- .../Savers/MovieNfoSaver.cs | 4 +- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Model.Signed.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- 27 files changed, 820 insertions(+), 593 deletions(-) create mode 100644 MediaBrowser.WebDashboard/Api/PackageCreator.cs diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 1fa1a5509d..65c51befff 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -237,7 +237,7 @@ namespace MediaBrowser.Api if (musicVideo != null) { - musicVideo.Artist = request.Artists[0]; + musicVideo.Artists = request.Artists.ToList(); musicVideo.Album = request.Album; } diff --git a/MediaBrowser.Api/Movies/TrailersService.cs b/MediaBrowser.Api/Movies/TrailersService.cs index b0ee6b6d57..a6024d4610 100644 --- a/MediaBrowser.Api/Movies/TrailersService.cs +++ b/MediaBrowser.Api/Movies/TrailersService.cs @@ -1,10 +1,20 @@ -using MediaBrowser.Controller.Dto; +using MediaBrowser.Api.UserLibrary; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; using ServiceStack; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace MediaBrowser.Api.Movies { @@ -16,6 +26,17 @@ namespace MediaBrowser.Api.Movies { } + [Route("/Trailers", "GET", Summary = "Finds movies and trailers similar to a given trailer.")] + public class Getrailers : BaseItemsRequest, IReturn + { + /// + /// Gets or sets the user id. + /// + /// The user id. + [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] + public Guid? UserId { get; set; } + } + /// /// Class TrailersService /// @@ -38,6 +59,7 @@ namespace MediaBrowser.Api.Movies private readonly IItemRepository _itemRepo; private readonly IDtoService _dtoService; + private readonly IChannelManager _channelManager; /// /// Initializes a new instance of the class. @@ -45,13 +67,14 @@ namespace MediaBrowser.Api.Movies /// The user manager. /// The user data repository. /// The library manager. - public TrailersService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService) + public TrailersService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, IChannelManager channelManager) { _userManager = userManager; _userDataRepository = userDataRepository; _libraryManager = libraryManager; _itemRepo = itemRepo; _dtoService = dtoService; + _channelManager = channelManager; } /// @@ -75,5 +98,71 @@ namespace MediaBrowser.Api.Movies return ToOptimizedSerializedResultUsingCache(result); } + + public async Task Get(Getrailers request) + { + var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null; + var result = await GetAllTrailers(user).ConfigureAwait(false); + + IEnumerable items = result.Items; + + // Apply filters + // Run them starting with the ones that are likely to reduce the list the most + foreach (var filter in request.GetFilters().OrderByDescending(f => (int)f)) + { + items = ItemsService.ApplyFilter(items, filter, user, _userDataRepository); + } + + items = _libraryManager.Sort(items, user, request.GetOrderBy(), request.SortOrder ?? SortOrder.Ascending); + + var itemsArray = items.ToList(); + + var pagedItems = ApplyPaging(request, itemsArray); + + var fields = request.GetItemFields().ToList(); + + var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray(); + + return new ItemsResult + { + TotalRecordCount = itemsArray.Count, + Items = returnItems + }; + } + + private IEnumerable ApplyPaging(Getrailers request, IEnumerable items) + { + // Start at + if (request.StartIndex.HasValue) + { + items = items.Skip(request.StartIndex.Value); + } + + // Return limit + if (request.Limit.HasValue) + { + items = items.Take(request.Limit.Value); + } + + return items; + } + + private async Task> GetAllTrailers(User user) + { + var trailerResult = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery + { + ContentTypes = new[] { ChannelMediaContentType.MovieExtra }, + ExtraTypes = new[] { ExtraType.Trailer }, + UserId = user.Id.ToString("N") + + }, CancellationToken.None).ConfigureAwait(false); + + + return new QueryResult + { + Items = trailerResult.Items, + TotalRecordCount = trailerResult.TotalRecordCount + }; + } } } diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 24b2bebd3a..8f45c95daa 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -97,7 +97,12 @@ namespace MediaBrowser.Api.Playback public bool ReadInputAtNativeFramerate { - get { return InputProtocol == MediaProtocol.Rtmp || string.Equals(InputContainer, "wtv", StringComparison.OrdinalIgnoreCase); } + get { + + return InputProtocol == MediaProtocol.Rtmp || + string.Equals(InputContainer, "wtv", StringComparison.OrdinalIgnoreCase) || + !string.IsNullOrEmpty(LiveTvStreamId); + } } public TransportStreamTimestamp InputTimestamp { get; set; } diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 897a68a65c..9cad64bfa4 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Collections; +using System.Threading; +using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -8,6 +9,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Channels; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using ServiceStack; diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 070572b9b2..2d9e052b1a 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -226,7 +226,9 @@ namespace MediaBrowser.Controller.Entities.Audio public IEnumerable GetTaggedItems(IEnumerable inputItems) { - return inputItems.OfType().Where(i => i.HasArtist(Name)).Cast(); + return inputItems.OfType() + .Where(i => i.HasArtist(Name)) + .Cast(); } } } diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs index d36bfd7c41..307117fdd4 100644 --- a/MediaBrowser.Controller/Entities/MusicVideo.cs +++ b/MediaBrowser.Controller/Entities/MusicVideo.cs @@ -11,12 +11,6 @@ namespace MediaBrowser.Controller.Entities { public class MusicVideo : Video, IHasArtist, IHasMusicGenres, IHasProductionLocations, IHasBudget, IHasLookupInfo { - /// - /// Gets or sets the artist. - /// - /// The artist. - public string Artist { get; set; } - /// /// Gets or sets the album. /// @@ -35,27 +29,12 @@ namespace MediaBrowser.Controller.Entities /// The revenue. public double? Revenue { get; set; } public List ProductionLocations { get; set; } + public List Artists { get; set; } public MusicVideo() { ProductionLocations = new List(); - } - - [IgnoreDataMember] - public List Artists - { - get - { - var list = new List(); - - if (!string.IsNullOrEmpty(Artist)) - { - list.Add(Artist); - } - - return list; - - } + Artists = new List(); } [IgnoreDataMember] @@ -63,15 +42,7 @@ namespace MediaBrowser.Controller.Entities { get { - var list = new List(); - - if (!string.IsNullOrEmpty(Artist)) - { - list.Add(Artist); - } - - return list; - + return Artists; } } @@ -82,7 +53,7 @@ namespace MediaBrowser.Controller.Entities /// true if the specified name has artist; otherwise, false. public bool HasArtist(string name) { - return string.Equals(Artist, name, StringComparison.OrdinalIgnoreCase); + return AllArtists.Contains(name, StringComparer.OrdinalIgnoreCase); } /// diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index 28aebae79e..6bc50b1ea9 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -671,10 +671,10 @@ namespace MediaBrowser.Dlna.Didl if (musicVideo != null) { - if (!string.IsNullOrEmpty(musicVideo.Artist)) + foreach (var artist in musicVideo.Artists) { - AddValue(element, "upnp", "artist", musicVideo.Artist, NS_UPNP); - AddAlbumArtist(element, musicVideo.Artist); + AddValue(element, "upnp", "artist", artist, NS_UPNP); + AddAlbumArtist(element, artist); } if (!string.IsNullOrEmpty(musicVideo.Album)) diff --git a/MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs index b88ff6c3a9..f695487c5e 100644 --- a/MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs @@ -1,7 +1,8 @@ -using System.Xml; -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; +using System; +using System.Xml; namespace MediaBrowser.LocalMetadata.Parsers { @@ -26,8 +27,17 @@ namespace MediaBrowser.LocalMetadata.Parsers switch (reader.Name) { case "Artist": - item.Artist = reader.ReadElementContentAsString(); - break; + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + var artists = val.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + item.Artists.AddRange(artists); + } + + break; + } case "Album": item.Album = reader.ReadElementContentAsString(); diff --git a/MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs index 40ed156bc5..84d41c8e2f 100644 --- a/MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs @@ -78,9 +78,9 @@ namespace MediaBrowser.LocalMetadata.Savers if (musicVideo != null) { - if (!string.IsNullOrEmpty(musicVideo.Artist)) + if (musicVideo.Artists.Count > 0) { - builder.Append("" + SecurityElement.Escape(musicVideo.Artist) + ""); + builder.Append("" + SecurityElement.Escape(string.Join(";", musicVideo.Artists.ToArray())) + ""); } if (!string.IsNullOrEmpty(musicVideo.Album)) { diff --git a/MediaBrowser.Model/ApiClient/IConnectionManager.cs b/MediaBrowser.Model/ApiClient/IConnectionManager.cs index c649ef756d..93aef7a7d8 100644 --- a/MediaBrowser.Model/ApiClient/IConnectionManager.cs +++ b/MediaBrowser.Model/ApiClient/IConnectionManager.cs @@ -30,7 +30,9 @@ namespace MediaBrowser.Model.ApiClient /// Occurs when [connect user sign out]. /// event EventHandler ConnectUserSignOut; - + [Obsolete] + event EventHandler RemoteLoggedOut; + /// /// Gets the connect user. /// diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 2e7dc2e400..230680ff25 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -180,6 +180,8 @@ namespace MediaBrowser.Model.Configuration public bool SaveMetadataHidden { get; set; } + public bool PlaylistImagesDeleted { get; set; } + /// /// Initializes a new instance of the class. /// diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index 80fda4bc2f..e37334c6c8 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -43,7 +43,8 @@ namespace MediaBrowser.Model.Dto public DateTime? DateCreated { get; set; } public DateTime? DateLastMediaAdded { get; set; } - + public ExtraType? ExtraType { get; set; } + public int? AirsBeforeSeasonNumber { get; set; } public int? AirsAfterSeasonNumber { get; set; } public int? AirsBeforeEpisodeNumber { get; set; } diff --git a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs index 1fa0ed05e0..bbb456b2b8 100644 --- a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs +++ b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs @@ -1,15 +1,13 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; +using System.Linq; namespace MediaBrowser.Providers.Music { @@ -36,9 +34,9 @@ namespace MediaBrowser.Providers.Music target.Album = source.Album; } - if (replaceData || string.IsNullOrEmpty(target.Artist)) + if (replaceData || target.Artists.Count == 0) { - target.Artist = source.Artist; + target.Artists = source.Artists.ToList(); } } } diff --git a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs index 206ba59224..1370b2bf29 100644 --- a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -3,8 +3,6 @@ using MediaBrowser.Common.Events; using MediaBrowser.Common.Implementations.Configuration; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Model.Configuration; @@ -215,9 +213,7 @@ namespace MediaBrowser.Server.Implementations.Configuration { DisableMetadataService(typeof(Movie), Configuration, service); DisableMetadataService(typeof(Episode), Configuration, service); - DisableMetadataService(typeof(Season), Configuration, service); DisableMetadataService(typeof(Series), Configuration, service); - DisableMetadataService(typeof(Video), Configuration, service); } private void DisableMetadataService(Type type, ServerConfiguration config, string service) diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 366a5558b1..3393876fce 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -72,22 +72,9 @@ namespace MediaBrowser.Server.Implementations.Dto if (byName != null && !(item is LiveTvChannel)) { - IEnumerable libraryItems; - - var artist = item as MusicArtist; - - if (artist == null || artist.IsAccessedByName) - { - libraryItems = user != null ? - user.RootFolder.GetRecursiveChildren(user) : - _libraryManager.RootFolder.RecursiveChildren; - } - else - { - libraryItems = user != null ? - artist.GetRecursiveChildren(user) : - artist.RecursiveChildren; - } + var libraryItems = user != null ? + user.RootFolder.GetRecursiveChildren(user) : + _libraryManager.RootFolder.RecursiveChildren; SetItemByNameInfo(item, dto, byName.GetTaggedItems(libraryItems).ToList(), user); @@ -398,7 +385,7 @@ namespace MediaBrowser.Server.Implementations.Dto } dto.Album = item.Album; - dto.Artists = string.IsNullOrEmpty(item.Artist) ? new List() : new List { item.Artist }; + dto.Artists = item.Artists; } private void SetGameProperties(BaseItemDto dto, Game item) @@ -1232,6 +1219,12 @@ namespace MediaBrowser.Server.Implementations.Dto dto.ChannelId = channelItem.ChannelId; dto.ChannelName = _channelManagerFactory().GetChannel(channelItem.ChannelId).Name; } + + var channelMediaItem = item as IChannelMediaItem; + if (channelMediaItem != null) + { + dto.ExtraType = channelMediaItem.ExtraType; + } } private void AttachLinkedChildImages(BaseItemDto dto, Folder folder, User user) diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index 6c2d756e4c..575b38c530 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -1247,5 +1247,10 @@ "OptionWeekdays": "Weekdays", "OptionWeekends": "Weekends", "MessageProfileInfoSynced": "User profile information synced with Media Browser Connect.", - "HeaderOptionalLinkMediaBrowserAccount": "Optional: Link your Media Browser account" + "HeaderOptionalLinkMediaBrowserAccount": "Optional: Link your Media Browser account", + "ButtonTrailerReel": "Trailer reel", + "HeaderTrailerReel": "Trailer Reel", + "OptionPlayUnwatchedTrailersOnly": "Play only unwatched trailers", + "HeaderTrailerReelHelp": "Start a trailer reel to play a long running playlist of trailers.", + "MessageNoTrailersFound": "No trailers found. Install the Trailer channel plugin to import a library of internet trailers." } diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index cf204a5baa..a0cd600672 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -1546,11 +1546,7 @@ namespace MediaBrowser.Server.Implementations.Session if (musicVideo != null) { info.Album = musicVideo.Album; - - if (!string.IsNullOrWhiteSpace(musicVideo.Artist)) - { - info.Artists.Add(musicVideo.Artist); - } + info.Artists = musicVideo.Artists.ToList(); } var backropItem = item.HasImage(ImageType.Backdrop) ? item : null; diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 2c873f9266..6ba5f4c532 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -324,6 +324,41 @@ namespace MediaBrowser.ServerApplication private void PerformVersionMigration() { DeleteDeprecatedModules(); + + if (!ServerConfigurationManager.Configuration.PlaylistImagesDeleted) + { + DeletePlaylistImages(); + ServerConfigurationManager.Configuration.PlaylistImagesDeleted = true; + ServerConfigurationManager.SaveConfiguration(); + } + } + + private void DeletePlaylistImages() + { + try + { + var path = Path.Combine(ApplicationPaths.DataPath, "playlists"); + + var files = Directory.GetFiles(path, "*", SearchOption.AllDirectories) + .Where(i => BaseItem.SupportedImageExtensions.Contains(Path.GetExtension(i) ?? string.Empty)) + .ToList(); + + foreach (var file in files) + { + try + { + File.Delete(file); + } + catch (IOException) + { + + } + } + } + catch (IOException) + { + + } } private void DeleteDeprecatedModules() diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 1b8e21e201..7f58a63bbd 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -14,10 +14,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using System.Threading.Tasks; -using WebMarkupMin.Core.Minifiers; -using WebMarkupMin.Core.Settings; namespace MediaBrowser.WebDashboard.Api { @@ -49,6 +46,12 @@ namespace MediaBrowser.WebDashboard.Api public string Name { get; set; } } + [Route("/web/Package", "GET")] + [Route("/dashboard/Package", "GET")] + public class GetDashboardPackage + { + } + /// /// Class GetDashboardResource /// @@ -120,35 +123,6 @@ namespace MediaBrowser.WebDashboard.Api _jsonSerializer = jsonSerializer; } - /// - /// Gets the dashboard UI path. - /// - /// The dashboard UI path. - public string DashboardUIPath - { - get - { - if (!string.IsNullOrEmpty(_serverConfigurationManager.Configuration.DashboardSourcePath)) - { - return _serverConfigurationManager.Configuration.DashboardSourcePath; - } - - var runningDirectory = Path.GetDirectoryName(_serverConfigurationManager.ApplicationPaths.ApplicationPath); - - return Path.Combine(runningDirectory, "dashboard-ui"); - } - } - - /// - /// Gets the dashboard resource path. - /// - /// The virtual path. - /// System.String. - private string GetDashboardResourcePath(string virtualPath) - { - return Path.Combine(DashboardUIPath, virtualPath.Replace('/', Path.DirectorySeparatorChar)); - } - /// /// Gets the specified request. /// @@ -158,7 +132,7 @@ namespace MediaBrowser.WebDashboard.Api { var page = ServerEntryPoint.Instance.PluginConfigurationPages.First(p => p.Name.Equals(request.Name, StringComparison.OrdinalIgnoreCase)); - return ResultFactory.GetStaticResult(Request, page.Plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => ModifyHtml(page.GetHtmlStream(), null)); + return ResultFactory.GetStaticResult(Request, page.Plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => GetPackageCreator().ModifyHtml(page.GetHtmlStream(), null)); } /// @@ -228,7 +202,7 @@ namespace MediaBrowser.WebDashboard.Api { Request.Response.Redirect("wizardstart.html"); return null; - } + } } path = path.Replace("scripts/jquery.mobile-1.4.4.min.map", "thirdparty/jquerymobile-1.4.4/jquery.mobile-1.4.4.min.map", StringComparison.OrdinalIgnoreCase); @@ -241,7 +215,7 @@ namespace MediaBrowser.WebDashboard.Api !contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) && !contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase)) { - return ResultFactory.GetResult(GetResourceStream(path, isHtml, localizationCulture).Result, contentType); + return ResultFactory.GetResult(GetResourceStream(path, localizationCulture).Result, contentType); } TimeSpan? cacheDuration = null; @@ -257,7 +231,7 @@ namespace MediaBrowser.WebDashboard.Api var cacheKey = (assembly.Version + (localizationCulture ?? string.Empty) + path).GetMD5(); - return ResultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(path, isHtml, localizationCulture)); + return ResultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(path, localizationCulture)); } private string GetLocalizationCulture() @@ -269,47 +243,17 @@ namespace MediaBrowser.WebDashboard.Api /// Gets the resource stream. /// /// The path. - /// if set to true [is HTML]. /// The localization culture. /// Task{Stream}. - private async Task GetResourceStream(string path, bool isHtml, string localizationCulture) + private Task GetResourceStream(string path, string localizationCulture) { - Stream resourceStream; - - if (path.Equals("scripts/all.js", StringComparison.OrdinalIgnoreCase)) - { - resourceStream = await GetAllJavascript().ConfigureAwait(false); - } - else if (path.Equals("css/all.css", StringComparison.OrdinalIgnoreCase)) - { - resourceStream = await GetAllCss().ConfigureAwait(false); - } - else - { - resourceStream = GetRawResourceStream(path); - } - - if (resourceStream != null) - { - // Don't apply any caching for html pages - // jQuery ajax doesn't seem to handle if-modified-since correctly - if (isHtml) - { - resourceStream = await ModifyHtml(resourceStream, localizationCulture).ConfigureAwait(false); - } - } - - return resourceStream; + return GetPackageCreator() + .GetResource(path, localizationCulture, _appHost.ApplicationVersion.ToString()); } - /// - /// Gets the raw resource stream. - /// - /// The path. - /// Task{Stream}. - private Stream GetRawResourceStream(string path) + private PackageCreator GetPackageCreator() { - return _fileSystem.GetFileStream(GetDashboardResourcePath(path), FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true); + return new PackageCreator(_fileSystem, _localization, Logger, _serverConfigurationManager, _jsonSerializer); } /// @@ -322,466 +266,67 @@ namespace MediaBrowser.WebDashboard.Api return Path.GetExtension(path).EndsWith("html", StringComparison.OrdinalIgnoreCase); } - /// - /// Modifies the HTML by adding common meta tags, css and js. - /// - /// The source stream. - /// The user identifier. - /// The localization culture. - /// Task{Stream}. - private async Task ModifyHtml(Stream sourceStream, string localizationCulture) + public async Task Get(GetDashboardPackage request) { - using (sourceStream) - { - string html; - - using (var memoryStream = new MemoryStream()) - { - await sourceStream.CopyToAsync(memoryStream).ConfigureAwait(false); - - html = Encoding.UTF8.GetString(memoryStream.ToArray()); - - if (!string.IsNullOrWhiteSpace(localizationCulture)) - { - var lang = localizationCulture.Split('-').FirstOrDefault(); - - html = _localization.LocalizeDocument(html, localizationCulture, GetLocalizationToken); - - html = html.Replace("", ""); - } - - //try - //{ - // var minifier = new HtmlMinifier(new HtmlMinificationSettings(true)); - - // html = minifier.Minify(html).MinifiedContent; - //} - //catch (Exception ex) - //{ - // Logger.ErrorException("Error minifying html", ex); - //} - } - - var version = GetType().Assembly.GetName().Version; - - html = html.Replace("", "" + GetMetaTags() + GetCommonCss(version) + GetCommonJavascript(version)); - - var bytes = Encoding.UTF8.GetBytes(html); - - return new MemoryStream(bytes); - } - } - - private string GetLocalizationToken(string phrase) - { - return "${" + phrase + "}"; - } - - /// - /// Gets the meta tags. - /// - /// System.String. - private static string GetMetaTags() - { - var sb = new StringBuilder(); - - sb.Append(""); - sb.Append(""); - //sb.Append(""); - sb.Append(""); - sb.Append(""); - //sb.Append(""); - - sb.Append(""); - - // http://developer.apple.com/library/ios/#DOCUMENTATION/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html - sb.Append(""); - sb.Append(""); - sb.Append(""); - sb.Append(""); - sb.Append(""); + var path = Path.Combine(_serverConfigurationManager.ApplicationPaths.ProgramDataPath, + "webclient-dump"); - return sb.ToString(); - } - - /// - /// Gets the common CSS. - /// - /// The version. - /// System.String. - private static string GetCommonCss(Version version) - { - var versionString = "?v=" + version; - - var files = new[] - { - "thirdparty/jquerymobile-1.4.4/jquery.mobile-1.4.4.min.css", - "thirdparty/swipebox-master/css/swipebox.min.css" + versionString, - "css/all.css" + versionString - }; - - var tags = files.Select(s => string.Format("", s)).ToArray(); - - return string.Join(string.Empty, tags); - } - - /// - /// Gets the common javascript. - /// - /// The version. - /// System.String. - private static string GetCommonJavascript(Version version) - { - var builder = new StringBuilder(); - - var versionString = "?v=" + version; - - var files = new[] - { - "scripts/all.js" + versionString, - "thirdparty/jstree1.0/jquery.jstree.min.js", - "thirdparty/swipebox-master/js/jquery.swipebox.min.js" + versionString - }; - - var tags = files.Select(s => string.Format("", s)).ToArray(); - - builder.Append(string.Join(string.Empty, tags)); - - return builder.ToString(); - } - - /// - /// Gets a stream containing all concatenated javascript - /// - /// Task{Stream}. - private async Task GetAllJavascript() - { - var memoryStream = new MemoryStream(); - var newLineBytes = Encoding.UTF8.GetBytes(Environment.NewLine); - - // jQuery + jQuery mobile - await AppendResource(memoryStream, "thirdparty/jquery-2.1.1.min.js", newLineBytes).ConfigureAwait(false); - await AppendResource(memoryStream, "thirdparty/jquerymobile-1.4.4/jquery.mobile-1.4.4.min.js", newLineBytes).ConfigureAwait(false); - - await AppendResource(memoryStream, "thirdparty/jquery.unveil-custom.js", newLineBytes).ConfigureAwait(false); - - // This script produces errors in older versions of safari - if ((Request.UserAgent ?? string.Empty).IndexOf("chrome/", StringComparison.OrdinalIgnoreCase) != -1) - { - await AppendResource(memoryStream, "thirdparty/cast_sender.js", newLineBytes).ConfigureAwait(false); - } - - await AppendLocalization(memoryStream).ConfigureAwait(false); - await memoryStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false); - - // Write the version string for the dashboard comparison function - var versionString = string.Format("window.dashboardVersion='{0}';", _appHost.ApplicationVersion); - var versionBytes = Encoding.UTF8.GetBytes(versionString); - - await memoryStream.WriteAsync(versionBytes, 0, versionBytes.Length).ConfigureAwait(false); - await memoryStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false); - - var builder = new StringBuilder(); - - foreach (var file in new[] - { - "thirdparty/apiclient/sha1.js", - "thirdparty/apiclient/mediabrowser.apiclient.js", - "thirdparty/apiclient/connectionmanager.js" - }) + try { - using (var fs = _fileSystem.GetFileStream(GetDashboardResourcePath(file), FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) - { - using (var streamReader = new StreamReader(fs)) - { - var text = await streamReader.ReadToEndAsync().ConfigureAwait(false); - builder.Append(text); - builder.Append(Environment.NewLine); - } - } + Directory.Delete(path, true); } - - foreach (var file in GetScriptFiles()) + catch (IOException) { - var path = GetDashboardResourcePath("scripts/" + file); - using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) - { - using (var streamReader = new StreamReader(fs)) - { - var text = await streamReader.ReadToEndAsync().ConfigureAwait(false); - builder.Append(text); - builder.Append(Environment.NewLine); - } - } } - var js = builder.ToString(); + var creator = GetPackageCreator(); - try - { - var result = new CrockfordJsMinifier().Minify(js, false, Encoding.UTF8); + CopyDirectory(creator.DashboardUIPath, path); - js = result.MinifiedContent; - } - catch (Exception ex) - { - Logger.ErrorException("Error minifying javascript", ex); - } + var culture = "en-US"; - var bytes = Encoding.UTF8.GetBytes(js); - await memoryStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); + await DumpHtml(creator.DashboardUIPath, path, culture, _appHost.ApplicationVersion.ToString()); - memoryStream.Position = 0; - return memoryStream; + return ""; } - private IEnumerable GetScriptFiles() + private async Task DumpHtml(string source, string destination, string culture, string appVersion) { - return new[] - { - "extensions.js", - "site.js", - "librarybrowser.js", - "librarylist.js", - "editorsidebar.js", - "librarymenu.js", - "mediacontroller.js", - "chromecast.js", - "backdrops.js", - "sync.js", - "playlistmanager.js", - - "mediaplayer.js", - "mediaplayer-video.js", - "nowplayingbar.js", - "nowplayingpage.js", - - "ratingdialog.js", - "aboutpage.js", - "alphapicker.js", - "addpluginpage.js", - "advancedconfigurationpage.js", - "metadataadvanced.js", - "autoorganizetv.js", - "autoorganizelog.js", - "channels.js", - "channelslatest.js", - "channelitems.js", - "channelsettings.js", - "dashboardgeneral.js", - "dashboardpage.js", - "dashboardsync.js", - "device.js", - "devices.js", - "devicesupload.js", - "directorybrowser.js", - "dlnaprofile.js", - "dlnaprofiles.js", - "dlnasettings.js", - "dlnaserversettings.js", - "editcollectionitems.js", - "edititemmetadata.js", - "edititemimages.js", - "edititemsubtitles.js", - - "playbackconfiguration.js", - "cinemamodeconfiguration.js", - "encodingsettings.js", - - "externalplayer.js", - "favorites.js", - "gamesrecommendedpage.js", - "gamesystemspage.js", - "gamespage.js", - "gamegenrepage.js", - "gamestudiospage.js", - "homelatest.js", - "indexpage.js", - "itembynamedetailpage.js", - "itemdetailpage.js", - "itemgallery.js", - "itemlistpage.js", - "librarypathmapping.js", - "reports.js", - "librarysettings.js", - "livetvchannel.js", - "livetvchannels.js", - "livetvguide.js", - "livetvnewrecording.js", - "livetvprogram.js", - "livetvrecording.js", - "livetvrecordinglist.js", - "livetvrecordings.js", - "livetvtimer.js", - "livetvseriestimer.js", - "livetvseriestimers.js", - "livetvsettings.js", - "livetvsuggested.js", - "livetvstatus.js", - "livetvtimers.js", - - "loginpage.js", - "logpage.js", - "medialibrarypage.js", - "metadataconfigurationpage.js", - "metadataimagespage.js", - "metadatasubtitles.js", - "metadatakodi.js", - "moviegenres.js", - "moviecollections.js", - "movies.js", - "movieslatest.js", - "moviepeople.js", - "moviesrecommended.js", - "moviestudios.js", - "movietrailers.js", - "musicalbums.js", - "musicalbumartists.js", - "musicartists.js", - "musicgenres.js", - "musicrecommended.js", - "musicvideos.js", - - "mypreferencesdisplay.js", - "mypreferenceslanguages.js", - "mypreferenceswebclient.js", - - "notifications.js", - "notificationlist.js", - "notificationsetting.js", - "notificationsettings.js", - "playlist.js", - "playlists.js", - "playlistedit.js", - - "plugincatalogpage.js", - "pluginspage.js", - "remotecontrol.js", - "scheduledtaskpage.js", - "scheduledtaskspage.js", - "search.js", - "serversecurity.js", - "songs.js", - "supporterkeypage.js", - "supporterpage.js", - "episodes.js", - "thememediaplayer.js", - "tvgenres.js", - "tvlatest.js", - "tvpeople.js", - "tvrecommended.js", - "tvshows.js", - "tvstudios.js", - "tvupcoming.js", - "useredit.js", - "userpassword.js", - "myprofile.js", - "userprofilespage.js", - "userparentalcontrol.js", - "userlibraryaccess.js", - "wizardfinishpage.js", - "wizardservice.js", - "wizardstartpage.js", - "wizardsettings.js", - "wizarduserpage.js" - }; + foreach (var file in Directory.GetFiles(source, "*.html", SearchOption.TopDirectoryOnly)) + { + await DumpHtmlFile(file, destination, culture, appVersion).ConfigureAwait(false); + } } - private async Task AppendLocalization(Stream stream) + private async Task DumpHtmlFile(string file, string destination, string culture, string appVersion) { - var js = "window.localizationGlossary=" + _jsonSerializer.SerializeToString(_localization.GetJavaScriptLocalizationDictionary(GetLocalizationCulture())); + var filename = Path.GetFileName(file); - var bytes = Encoding.UTF8.GetBytes(js); - await stream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); - } + var targetPath = Path.Combine(destination, filename); - /// - /// Gets all CSS. - /// - /// Task{Stream}. - private async Task GetAllCss() - { - var files = new[] - { - "site.css", - "chromecast.css", - "mediaplayer.css", - "mediaplayer-video.css", - "librarymenu.css", - "librarybrowser.css", - "detailtable.css", - "card.css", - "tileitem.css", - "metadataeditor.css", - "notifications.css", - "search.css", - "pluginupdates.css", - "remotecontrol.css", - "userimage.css", - "livetv.css", - "nowplaying.css", - "icons.css" - }; - - var builder = new StringBuilder(); - - foreach (var file in files) + using (var stream = await GetPackageCreator().GetResource(filename, culture, appVersion).ConfigureAwait(false)) { - var path = GetDashboardResourcePath("css/" + file); - - using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) + using (var fs = _fileSystem.GetFileStream(targetPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { - using (var streamReader = new StreamReader(fs)) - { - var text = await streamReader.ReadToEndAsync().ConfigureAwait(false); - builder.Append(text); - builder.Append(Environment.NewLine); - } + stream.CopyTo(fs); } } - - var css = builder.ToString(); - - //try - //{ - // var result = new KristensenCssMinifier().Minify(builder.ToString(), false, Encoding.UTF8); - - // css = result.MinifiedContent; - //} - //catch (Exception ex) - //{ - // Logger.ErrorException("Error minifying css", ex); - //} - - var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(css)); - - memoryStream.Position = 0; - return memoryStream; } - /// - /// Appends the resource. - /// - /// The output stream. - /// The path. - /// The new line bytes. - /// Task. - private async Task AppendResource(Stream outputStream, string path, byte[] newLineBytes) + private void CopyDirectory(string source, string destination) { - path = GetDashboardResourcePath(path); + Directory.CreateDirectory(destination); - using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) - { - using (var streamReader = new StreamReader(fs)) - { - var text = await streamReader.ReadToEndAsync().ConfigureAwait(false); - var bytes = Encoding.UTF8.GetBytes(text); - await outputStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); - } - } + //Now Create all of the directories + foreach (string dirPath in Directory.GetDirectories(source, "*", + SearchOption.AllDirectories)) + Directory.CreateDirectory(dirPath.Replace(source, destination)); - await outputStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false); + //Copy all the files & Replaces any files with the same name + foreach (string newPath in Directory.GetFiles(source, "*.*", + SearchOption.AllDirectories)) + File.Copy(newPath, newPath.Replace(source, destination), true); } } diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs new file mode 100644 index 0000000000..c1316223e0 --- /dev/null +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -0,0 +1,574 @@ +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Localization; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WebMarkupMin.Core.Minifiers; + +namespace MediaBrowser.WebDashboard.Api +{ + public class PackageCreator + { + private readonly IFileSystem _fileSystem; + private readonly ILocalizationManager _localization; + private readonly ILogger _logger; + private readonly IServerConfigurationManager _config; + private readonly IJsonSerializer _jsonSerializer; + + public PackageCreator(IFileSystem fileSystem, ILocalizationManager localization, ILogger logger, IServerConfigurationManager config, IJsonSerializer jsonSerializer) + { + _fileSystem = fileSystem; + _localization = localization; + _logger = logger; + _config = config; + _jsonSerializer = jsonSerializer; + } + + public async Task GetResource(string path, + string localizationCulture, + string appVersion) + { + var isHtml = IsHtml(path); + + Stream resourceStream; + + if (path.Equals("scripts/all.js", StringComparison.OrdinalIgnoreCase)) + { + resourceStream = await GetAllJavascript(localizationCulture, appVersion).ConfigureAwait(false); + } + else if (path.Equals("css/all.css", StringComparison.OrdinalIgnoreCase)) + { + resourceStream = await GetAllCss().ConfigureAwait(false); + } + else + { + resourceStream = GetRawResourceStream(path); + } + + if (resourceStream != null) + { + // Don't apply any caching for html pages + // jQuery ajax doesn't seem to handle if-modified-since correctly + if (isHtml) + { + resourceStream = await ModifyHtml(resourceStream, localizationCulture).ConfigureAwait(false); + } + } + + return resourceStream; + } + + /// + /// Determines whether the specified path is HTML. + /// + /// The path. + /// true if the specified path is HTML; otherwise, false. + private bool IsHtml(string path) + { + return Path.GetExtension(path).EndsWith("html", StringComparison.OrdinalIgnoreCase); + } + + /// + /// Gets the dashboard UI path. + /// + /// The dashboard UI path. + public string DashboardUIPath + { + get + { + if (!string.IsNullOrEmpty(_config.Configuration.DashboardSourcePath)) + { + return _config.Configuration.DashboardSourcePath; + } + + var runningDirectory = Path.GetDirectoryName(_config.ApplicationPaths.ApplicationPath); + + return Path.Combine(runningDirectory, "dashboard-ui"); + } + } + + /// + /// Gets the dashboard resource path. + /// + /// The virtual path. + /// System.String. + private string GetDashboardResourcePath(string virtualPath) + { + return Path.Combine(DashboardUIPath, virtualPath.Replace('/', Path.DirectorySeparatorChar)); + } + + /// + /// Modifies the HTML by adding common meta tags, css and js. + /// + /// The source stream. + /// The localization culture. + /// Task{Stream}. + public async Task ModifyHtml(Stream sourceStream, string localizationCulture) + { + using (sourceStream) + { + string html; + + using (var memoryStream = new MemoryStream()) + { + await sourceStream.CopyToAsync(memoryStream).ConfigureAwait(false); + + html = Encoding.UTF8.GetString(memoryStream.ToArray()); + + if (!string.IsNullOrWhiteSpace(localizationCulture)) + { + var lang = localizationCulture.Split('-').FirstOrDefault(); + + html = _localization.LocalizeDocument(html, localizationCulture, GetLocalizationToken); + + html = html.Replace("", ""); + } + + //try + //{ + // var minifier = new HtmlMinifier(new HtmlMinificationSettings(true)); + + // html = minifier.Minify(html).MinifiedContent; + //} + //catch (Exception ex) + //{ + // Logger.ErrorException("Error minifying html", ex); + //} + } + + var version = GetType().Assembly.GetName().Version; + + html = html.Replace("", "" + GetMetaTags() + GetCommonCss(version) + GetCommonJavascript(version)); + + var bytes = Encoding.UTF8.GetBytes(html); + + return new MemoryStream(bytes); + } + } + + private string GetLocalizationToken(string phrase) + { + return "${" + phrase + "}"; + } + + /// + /// Gets the meta tags. + /// + /// System.String. + private static string GetMetaTags() + { + var sb = new StringBuilder(); + + sb.Append(""); + sb.Append(""); + //sb.Append(""); + sb.Append(""); + sb.Append(""); + //sb.Append(""); + + sb.Append(""); + + // http://developer.apple.com/library/ios/#DOCUMENTATION/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html + sb.Append(""); + sb.Append(""); + sb.Append(""); + sb.Append(""); + sb.Append(""); + + return sb.ToString(); + } + + /// + /// Gets the common CSS. + /// + /// The version. + /// System.String. + private string GetCommonCss(Version version) + { + var versionString = "?v=" + version; + + var files = new[] + { + "thirdparty/jquerymobile-1.4.4/jquery.mobile-1.4.4.min.css", + "thirdparty/swipebox-master/css/swipebox.min.css" + versionString, + "css/all.css" + versionString + }; + + var tags = files.Select(s => string.Format("", s)).ToArray(); + + return string.Join(string.Empty, tags); + } + + /// + /// Gets the common javascript. + /// + /// The version. + /// System.String. + private string GetCommonJavascript(Version version) + { + var builder = new StringBuilder(); + + var versionString = "?v=" + version; + + var files = new[] + { + "scripts/all.js" + versionString, + "thirdparty/jstree1.0/jquery.jstree.min.js", + "thirdparty/swipebox-master/js/jquery.swipebox.min.js" + versionString + }; + + var tags = files.Select(s => string.Format("", s)).ToArray(); + + builder.Append(string.Join(string.Empty, tags)); + + return builder.ToString(); + } + + /// + /// Gets a stream containing all concatenated javascript + /// + /// Task{Stream}. + private async Task GetAllJavascript(string culture, string version) + { + var memoryStream = new MemoryStream(); + var newLineBytes = Encoding.UTF8.GetBytes(Environment.NewLine); + + // jQuery + jQuery mobile + await AppendResource(memoryStream, "thirdparty/jquery-2.1.1.min.js", newLineBytes).ConfigureAwait(false); + await AppendResource(memoryStream, "thirdparty/jquerymobile-1.4.4/jquery.mobile-1.4.4.min.js", newLineBytes).ConfigureAwait(false); + + await AppendResource(memoryStream, "thirdparty/jquery.unveil-custom.js", newLineBytes).ConfigureAwait(false); + + await AppendResource(memoryStream, "thirdparty/cast_sender.js", newLineBytes).ConfigureAwait(false); + + await AppendLocalization(memoryStream, culture).ConfigureAwait(false); + await memoryStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false); + + // Write the version string for the dashboard comparison function + var versionString = string.Format("window.dashboardVersion='{0}';", version); + var versionBytes = Encoding.UTF8.GetBytes(versionString); + + await memoryStream.WriteAsync(versionBytes, 0, versionBytes.Length).ConfigureAwait(false); + await memoryStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false); + + var builder = new StringBuilder(); + + foreach (var file in new[] + { + "thirdparty/apiclient/sha1.js", + "thirdparty/apiclient/mediabrowser.apiclient.js", + "thirdparty/apiclient/connectionmanager.js" + }) + { + using (var fs = _fileSystem.GetFileStream(GetDashboardResourcePath(file), FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) + { + using (var streamReader = new StreamReader(fs)) + { + var text = await streamReader.ReadToEndAsync().ConfigureAwait(false); + builder.Append(text); + builder.Append(Environment.NewLine); + } + } + } + + foreach (var file in GetScriptFiles()) + { + var path = GetDashboardResourcePath("scripts/" + file); + + using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) + { + using (var streamReader = new StreamReader(fs)) + { + var text = await streamReader.ReadToEndAsync().ConfigureAwait(false); + builder.Append(text); + builder.Append(Environment.NewLine); + } + } + } + + var js = builder.ToString(); + + try + { + var result = new CrockfordJsMinifier().Minify(js, false, Encoding.UTF8); + + js = result.MinifiedContent; + } + catch (Exception ex) + { + _logger.ErrorException("Error minifying javascript", ex); + } + + var bytes = Encoding.UTF8.GetBytes(js); + await memoryStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); + + memoryStream.Position = 0; + return memoryStream; + } + private IEnumerable GetScriptFiles() + { + return new[] + { + "extensions.js", + "site.js", + "librarybrowser.js", + "librarylist.js", + "editorsidebar.js", + "librarymenu.js", + "mediacontroller.js", + "chromecast.js", + "backdrops.js", + "sync.js", + "playlistmanager.js", + + "mediaplayer.js", + "mediaplayer-video.js", + "nowplayingbar.js", + "nowplayingpage.js", + + "ratingdialog.js", + "aboutpage.js", + "alphapicker.js", + "addpluginpage.js", + "advancedconfigurationpage.js", + "metadataadvanced.js", + "autoorganizetv.js", + "autoorganizelog.js", + "channels.js", + "channelslatest.js", + "channelitems.js", + "channelsettings.js", + "dashboardgeneral.js", + "dashboardpage.js", + "dashboardsync.js", + "device.js", + "devices.js", + "devicesupload.js", + "directorybrowser.js", + "dlnaprofile.js", + "dlnaprofiles.js", + "dlnasettings.js", + "dlnaserversettings.js", + "editcollectionitems.js", + "edititemmetadata.js", + "edititemimages.js", + "edititemsubtitles.js", + + "playbackconfiguration.js", + "cinemamodeconfiguration.js", + "encodingsettings.js", + + "externalplayer.js", + "favorites.js", + "gamesrecommendedpage.js", + "gamesystemspage.js", + "gamespage.js", + "gamegenrepage.js", + "gamestudiospage.js", + "homelatest.js", + "indexpage.js", + "itembynamedetailpage.js", + "itemdetailpage.js", + "itemgallery.js", + "itemlistpage.js", + "librarypathmapping.js", + "reports.js", + "librarysettings.js", + "livetvchannel.js", + "livetvchannels.js", + "livetvguide.js", + "livetvnewrecording.js", + "livetvprogram.js", + "livetvrecording.js", + "livetvrecordinglist.js", + "livetvrecordings.js", + "livetvtimer.js", + "livetvseriestimer.js", + "livetvseriestimers.js", + "livetvsettings.js", + "livetvsuggested.js", + "livetvstatus.js", + "livetvtimers.js", + + "loginpage.js", + "logpage.js", + "medialibrarypage.js", + "metadataconfigurationpage.js", + "metadataimagespage.js", + "metadatasubtitles.js", + "metadatakodi.js", + "moviegenres.js", + "moviecollections.js", + "movies.js", + "movieslatest.js", + "moviepeople.js", + "moviesrecommended.js", + "moviestudios.js", + "movietrailers.js", + "musicalbums.js", + "musicalbumartists.js", + "musicartists.js", + "musicgenres.js", + "musicrecommended.js", + "musicvideos.js", + + "mypreferencesdisplay.js", + "mypreferenceslanguages.js", + "mypreferenceswebclient.js", + + "notifications.js", + "notificationlist.js", + "notificationsetting.js", + "notificationsettings.js", + "playlist.js", + "playlists.js", + "playlistedit.js", + + "plugincatalogpage.js", + "pluginspage.js", + "remotecontrol.js", + "scheduledtaskpage.js", + "scheduledtaskspage.js", + "search.js", + "serversecurity.js", + "songs.js", + "supporterkeypage.js", + "supporterpage.js", + "episodes.js", + "thememediaplayer.js", + "tvgenres.js", + "tvlatest.js", + "tvpeople.js", + "tvrecommended.js", + "tvshows.js", + "tvstudios.js", + "tvupcoming.js", + "useredit.js", + "userpassword.js", + "myprofile.js", + "userprofilespage.js", + "userparentalcontrol.js", + "userlibraryaccess.js", + "wizardfinishpage.js", + "wizardservice.js", + "wizardstartpage.js", + "wizardsettings.js", + "wizarduserpage.js" + }; + } + + private async Task AppendLocalization(Stream stream, string culture) + { + var js = "window.localizationGlossary=" + _jsonSerializer.SerializeToString(_localization.GetJavaScriptLocalizationDictionary(culture)); + + var bytes = Encoding.UTF8.GetBytes(js); + await stream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); + } + + /// + /// Appends the resource. + /// + /// The output stream. + /// The path. + /// The new line bytes. + /// Task. + private async Task AppendResource(Stream outputStream, string path, byte[] newLineBytes) + { + path = GetDashboardResourcePath(path); + + using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) + { + using (var streamReader = new StreamReader(fs)) + { + var text = await streamReader.ReadToEndAsync().ConfigureAwait(false); + var bytes = Encoding.UTF8.GetBytes(text); + await outputStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); + } + } + + await outputStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false); + } + + + /// + /// Gets all CSS. + /// + /// Task{Stream}. + private async Task GetAllCss() + { + var files = new[] + { + "site.css", + "chromecast.css", + "mediaplayer.css", + "mediaplayer-video.css", + "librarymenu.css", + "librarybrowser.css", + "detailtable.css", + "card.css", + "tileitem.css", + "metadataeditor.css", + "notifications.css", + "search.css", + "pluginupdates.css", + "remotecontrol.css", + "userimage.css", + "livetv.css", + "nowplaying.css", + "icons.css" + }; + + var builder = new StringBuilder(); + + foreach (var file in files) + { + var path = GetDashboardResourcePath("css/" + file); + + using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) + { + using (var streamReader = new StreamReader(fs)) + { + var text = await streamReader.ReadToEndAsync().ConfigureAwait(false); + builder.Append(text); + builder.Append(Environment.NewLine); + } + } + } + + var css = builder.ToString(); + + //try + //{ + // var result = new KristensenCssMinifier().Minify(builder.ToString(), false, Encoding.UTF8); + + // css = result.MinifiedContent; + //} + //catch (Exception ex) + //{ + // Logger.ErrorException("Error minifying css", ex); + //} + + var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(css)); + + memoryStream.Position = 0; + return memoryStream; + } + + /// + /// Gets the raw resource stream. + /// + /// The path. + /// Task{Stream}. + private Stream GetRawResourceStream(string path) + { + return _fileSystem.GetFileStream(GetDashboardResourcePath(path), FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true); + } + + } +} diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index e80c2c3753..719b1abd81 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -67,6 +67,7 @@ + diff --git a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs index d2bfeeb7ed..7cf987f9c6 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs @@ -66,7 +66,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val) && movie != null) { - movie.Artist = val; + movie.Artists.Add(val); } break; diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs index a4e8544ed9..ce913c524d 100644 --- a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs @@ -74,9 +74,9 @@ namespace MediaBrowser.XbmcMetadata.Savers if (musicVideo != null) { - if (!string.IsNullOrEmpty(musicVideo.Artist)) + foreach (var artist in musicVideo.Artists) { - writer.WriteElementString("artist", musicVideo.Artist); + writer.WriteElementString("artist", artist); } if (!string.IsNullOrEmpty(musicVideo.Album)) { diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index d2ce27c83e..dc425327e8 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.496 + 3.0.497 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 8e6fcc097d..c1de3d8842 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.496 + 3.0.497 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index f87b967fb1..887e35a577 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Model.Signed - 3.0.496 + 3.0.497 MediaBrowser.Model - Signed Edition Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 028013454e..172ab335af 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.496 + 3.0.497 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 df509dedb71c35d6503a46d0e87a40790d2401e6 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 20 Oct 2014 20:54:01 -0400 Subject: [PATCH 2/9] update dlna profiles --- MediaBrowser.Dlna/DlnaManager.cs | 103 +++++++++++------- MediaBrowser.Dlna/Profiles/XboxOneProfile.cs | 10 ++ MediaBrowser.Dlna/Profiles/Xml/Android.xml | 29 +++-- MediaBrowser.Dlna/Profiles/Xml/Default.xml | 2 + MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml | 2 + .../Profiles/Xml/DirecTV HD-DVR.xml | 2 + .../Profiles/Xml/Dish Hopper-Joey.xml | 2 + .../Profiles/Xml/LG Smart TV.xml | 2 + .../Profiles/Xml/Linksys DMA2100.xml | 2 + .../Profiles/Xml/MediaMonkey.xml | 2 + .../Profiles/Xml/Panasonic Viera.xml | 2 + .../Profiles/Xml/Samsung Smart TV.xml | 2 + .../Profiles/Xml/Sony Blu-ray Player 2013.xml | 2 + .../Profiles/Xml/Sony Blu-ray Player.xml | 2 + .../Profiles/Xml/Sony Bravia (2010).xml | 2 + .../Profiles/Xml/Sony Bravia (2011).xml | 2 + .../Profiles/Xml/Sony Bravia (2012).xml | 2 + .../Profiles/Xml/Sony Bravia (2013).xml | 2 + .../Profiles/Xml/Sony PlayStation 3.xml | 2 + MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml | 2 + .../Profiles/Xml/Windows 8 RT.xml | 2 + .../Profiles/Xml/Windows Phone.xml | 2 + MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml | 2 + MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml | 18 +-- MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml | 2 + .../Dlna/Profiles/AndroidProfile.cs | 10 +- .../Api/DashboardService.cs | 28 +++-- 27 files changed, 174 insertions(+), 66 deletions(-) diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs index 1a5269b9db..b4519d02de 100644 --- a/MediaBrowser.Dlna/DlnaManager.cs +++ b/MediaBrowser.Dlna/DlnaManager.cs @@ -3,6 +3,7 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; +using MediaBrowser.Controller.Plugins; using MediaBrowser.Dlna.Profiles; using MediaBrowser.Dlna.Server; using MediaBrowser.Model.Dlna; @@ -37,8 +38,6 @@ namespace MediaBrowser.Dlna _appPaths = appPaths; _logger = logger; _jsonSerializer = jsonSerializer; - - //DumpProfiles(); } public IEnumerable GetProfiles() @@ -55,44 +54,6 @@ namespace MediaBrowser.Dlna return list; } - private void DumpProfiles() - { - var list = new List - { - new SamsungSmartTvProfile(), - new Xbox360Profile(), - new XboxOneProfile(), - new SonyPs3Profile(), - new SonyBravia2010Profile(), - new SonyBravia2011Profile(), - new SonyBravia2012Profile(), - new SonyBravia2013Profile(), - new SonyBlurayPlayer2013Profile(), - new SonyBlurayPlayerProfile(), - new PanasonicVieraProfile(), - new WdtvLiveProfile(), - new DenonAvrProfile(), - new LinksysDMA2100Profile(), - new LgTvProfile(), - new Foobar2000Profile(), - new MediaMonkeyProfile(), - new Windows81Profile(), - //new WindowsMediaCenterProfile(), - new WindowsPhoneProfile(), - new AndroidProfile(true, true, new[]{"baseline", "constrained baseline"}), - new DirectTvProfile(), - new DishHopperJoeyProfile(), - new DefaultProfile() - }; - - foreach (var item in list) - { - var path = Path.Combine(_appPaths.ProgramDataPath, _fileSystem.GetValidFilename(item.Name) + ".xml"); - - _xmlSerializer.SerializeToFile(item, path); - } - } - private bool _extracted; private readonly object _syncLock = new object(); private void ExtractProfilesIfNeeded() @@ -521,4 +482,66 @@ namespace MediaBrowser.Dlna }; } } + + class DlnaProfileEntryPoint : IServerEntryPoint + { + private readonly IApplicationPaths _appPaths; + private readonly IXmlSerializer _xmlSerializer; + private readonly IFileSystem _fileSystem; + + public DlnaProfileEntryPoint(IApplicationPaths appPaths, IXmlSerializer xmlSerializer, IFileSystem fileSystem) + { + _appPaths = appPaths; + _xmlSerializer = xmlSerializer; + _fileSystem = fileSystem; + } + + public void Run() + { + //DumpProfiles(); + } + + private void DumpProfiles() + { + var list = new List + { + new SamsungSmartTvProfile(), + new Xbox360Profile(), + new XboxOneProfile(), + new SonyPs3Profile(), + new SonyBravia2010Profile(), + new SonyBravia2011Profile(), + new SonyBravia2012Profile(), + new SonyBravia2013Profile(), + new SonyBlurayPlayer2013Profile(), + new SonyBlurayPlayerProfile(), + new PanasonicVieraProfile(), + new WdtvLiveProfile(), + new DenonAvrProfile(), + new LinksysDMA2100Profile(), + new LgTvProfile(), + new Foobar2000Profile(), + new MediaMonkeyProfile(), + new Windows81Profile(), + //new WindowsMediaCenterProfile(), + new WindowsPhoneProfile(), + new AndroidProfile(), + new DirectTvProfile(), + new DishHopperJoeyProfile(), + new DefaultProfile() + }; + + foreach (var item in list) + { + var path = Path.Combine(_appPaths.ProgramDataPath, _fileSystem.GetValidFilename(item.Name) + ".xml"); + + _xmlSerializer.SerializeToFile(item, path); + } + } + + public void Dispose() + { + throw new NotImplementedException(); + } + } } \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/XboxOneProfile.cs b/MediaBrowser.Dlna/Profiles/XboxOneProfile.cs index 292ee82c6c..f95d1b37e0 100644 --- a/MediaBrowser.Dlna/Profiles/XboxOneProfile.cs +++ b/MediaBrowser.Dlna/Profiles/XboxOneProfile.cs @@ -10,8 +10,18 @@ namespace MediaBrowser.Dlna.Profiles public XboxOneProfile() { Name = "Xbox One"; + + ModelName = "Windows Media Player Sharing"; + ModelNumber = "12.0"; + ModelUrl = "http://www.microsoft.com/"; + Manufacturer = "Microsoft Corporation"; + ManufacturerUrl = "http://www.microsoft.com/"; XDlnaDoc = "DMS-1.50"; + TimelineOffsetSeconds = 40; + RequiresPlainFolders = true; + RequiresPlainVideoItems = true; + Identification = new DeviceIdentification { ModelName = "Xbox One", diff --git a/MediaBrowser.Dlna/Profiles/Xml/Android.xml b/MediaBrowser.Dlna/Profiles/Xml/Android.xml index 642f93c149..85f3c73a70 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Android.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Android.xml @@ -25,6 +25,8 @@ 0 false false + false + false @@ -36,33 +38,42 @@ - - + + - + - - + + + + + + + + + + + - + - + - - + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Default.xml b/MediaBrowser.Dlna/Profiles/Xml/Default.xml index cc1481ee33..468bd13d63 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Default.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Default.xml @@ -25,6 +25,8 @@ 0 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml index ffcdcf9fc8..1636f7202c 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml @@ -30,6 +30,8 @@ 0 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml b/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml index c6ca13e041..b5563e9e6b 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml @@ -31,6 +31,8 @@ 10 true true + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml b/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml index 98ab664082..ebd887670d 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml @@ -32,6 +32,8 @@ 0 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml b/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml index fd5363529c..8073e3ff0f 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml @@ -31,6 +31,8 @@ 10 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml index 7d0f416e90..d18501200d 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml @@ -29,6 +29,8 @@ 0 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml b/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml index eb85e58772..f62f020342 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml @@ -31,6 +31,8 @@ 0 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml b/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml index de767b9327..7960acf3d1 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml @@ -32,6 +32,8 @@ 10 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml index 25dac2f843..4ef0ec79bc 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml @@ -31,6 +31,8 @@ 0 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml index 65f3393ff2..107896ca27 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml @@ -31,6 +31,8 @@ 0 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml index c47e5045eb..10fdccb7ba 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml @@ -33,6 +33,8 @@ 0 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml index c0164ae533..3adcc0a34d 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml @@ -33,6 +33,8 @@ 0 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml index 454c7b794d..ad3f7f9e6d 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml @@ -33,6 +33,8 @@ 0 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml index f9e20531ad..b179b2154a 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml @@ -33,6 +33,8 @@ 0 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml index 68e35184c2..7c98069293 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml @@ -33,6 +33,8 @@ 0 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml index 103c8ad273..f4d5beb075 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml @@ -33,6 +33,8 @@ 0 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml index 3717b07c4c..b1fdd7f713 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml @@ -32,6 +32,8 @@ 5 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml b/MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml index 3ed3c14d96..154e7e8fce 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml @@ -29,6 +29,8 @@ 0 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml b/MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml index d1f4152918..1331f2f8e2 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml @@ -25,6 +25,8 @@ 0 false false + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml index 3a7eddc1f9..fc9b13eadf 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml @@ -32,6 +32,8 @@ 40 true true + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml index 13c368328b..ed0119b603 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml @@ -7,12 +7,12 @@ Media Browser - Media Browser - http://mediabrowser.tv/ - Media Browser + Microsoft Corporation + http://www.microsoft.com/ + Windows Media Player Sharing Media Browser - Media Browser - http://mediabrowser.tv/ + 12.0 + http://www.microsoft.com/ false false Audio,Photo,Video @@ -27,9 +27,11 @@ 128000 DMS-1.50 http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000 - 0 - false - false + 40 + true + true + false + false diff --git a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml index 81347accc0..8f341dc4ba 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml @@ -31,6 +31,8 @@ 0 false false + false + false diff --git a/MediaBrowser.Model/Dlna/Profiles/AndroidProfile.cs b/MediaBrowser.Model/Dlna/Profiles/AndroidProfile.cs index 0d060cda85..1f27e5991c 100644 --- a/MediaBrowser.Model/Dlna/Profiles/AndroidProfile.cs +++ b/MediaBrowser.Model/Dlna/Profiles/AndroidProfile.cs @@ -6,8 +6,14 @@ namespace MediaBrowser.Model.Dlna.Profiles [XmlRoot("Profile")] public class AndroidProfile : DefaultProfile { - public AndroidProfile(bool supportsHls, - bool supportsMpegDash, + public AndroidProfile() + : this(true, true, new[] { "baseline", "constrained baseline" }) + { + + } + + public AndroidProfile(bool supportsHls, + bool supportsMpegDash, string[] supportedH264Profiles) { Name = "Android"; diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 7f58a63bbd..85a3888474 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -286,8 +286,14 @@ namespace MediaBrowser.WebDashboard.Api var culture = "en-US"; - await DumpHtml(creator.DashboardUIPath, path, culture, _appHost.ApplicationVersion.ToString()); + var appVersion = _appHost.ApplicationVersion.ToString(); + await DumpHtml(creator.DashboardUIPath, path, culture, appVersion); + await DumpJs(creator.DashboardUIPath, path, culture, appVersion); + + await DumpFile("scripts/all.js", Path.Combine(path, "scripts", "all.js"), culture, appVersion).ConfigureAwait(false); + await DumpFile("css/all.css", Path.Combine(path, "css", "all.css"), culture, appVersion).ConfigureAwait(false); + return ""; } @@ -295,19 +301,27 @@ namespace MediaBrowser.WebDashboard.Api { foreach (var file in Directory.GetFiles(source, "*.html", SearchOption.TopDirectoryOnly)) { - await DumpHtmlFile(file, destination, culture, appVersion).ConfigureAwait(false); + var filename = Path.GetFileName(file); + + await DumpFile(filename, Path.Combine(destination, filename), culture, appVersion).ConfigureAwait(false); } } - private async Task DumpHtmlFile(string file, string destination, string culture, string appVersion) + private async Task DumpJs(string source, string destination, string culture, string appVersion) { - var filename = Path.GetFileName(file); + foreach (var file in Directory.GetFiles(source, "*.js", SearchOption.TopDirectoryOnly)) + { + var filename = Path.GetFileName(file); - var targetPath = Path.Combine(destination, filename); + await DumpFile("scripts/" + filename, Path.Combine(destination, "scripts", filename), culture, appVersion).ConfigureAwait(false); + } + } - using (var stream = await GetPackageCreator().GetResource(filename, culture, appVersion).ConfigureAwait(false)) + private async Task DumpFile(string resourceVirtualPath, string destinationFilePath, string culture, string appVersion) + { + using (var stream = await GetPackageCreator().GetResource(resourceVirtualPath, culture, appVersion).ConfigureAwait(false)) { - using (var fs = _fileSystem.GetFileStream(targetPath, FileMode.Create, FileAccess.Write, FileShare.Read)) + using (var fs = _fileSystem.GetFileStream(destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.Read)) { stream.CopyTo(fs); } From a3d7849c268ad50f1553049124337be51fbc38a4 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 20 Oct 2014 23:41:11 -0400 Subject: [PATCH 3/9] support url's after closing nfo tag --- .../BaseApplicationHost.cs | 10 +++++ .../Entities/MusicVideo.cs | 15 +++++++ MediaBrowser.Dlna/DlnaManager.cs | 1 - .../Connect/ConnectEntryPoint.cs | 2 +- .../Parsers/BaseNfoParser.cs | 41 +++++++++++++++---- 5 files changed, 58 insertions(+), 11 deletions(-) diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs index 5a5d48ac27..a5b8082262 100644 --- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs +++ b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs @@ -296,6 +296,16 @@ namespace MediaBrowser.Common.Implementations logger.Info("64-Bit Process: {0}", Environment.Is64BitProcess); logger.Info("Program data path: {0}", appPaths.ProgramDataPath); + Type type = Type.GetType("Mono.Runtime"); + if (type != null) + { + MethodInfo displayName = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static); + if (displayName != null) + { + logger.Info("Mono: " + displayName.Invoke(null, null)); + } + } + logger.Info("Application Path: {0}", appPaths.ApplicationPath); logger.Info("*** When reporting issues please include the entire log file. ***".ToUpper()); diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs index 307117fdd4..015e4b4aee 100644 --- a/MediaBrowser.Controller/Entities/MusicVideo.cs +++ b/MediaBrowser.Controller/Entities/MusicVideo.cs @@ -46,6 +46,21 @@ namespace MediaBrowser.Controller.Entities } } + /// + /// TODO: Remove + /// + public string Artist + { + get { return Artists.FirstOrDefault(); } + set + { + if (!string.IsNullOrEmpty(value) && !Artists.Contains(value, StringComparer.OrdinalIgnoreCase)) + { + Artists.Add(value); + } + } + } + /// /// Determines whether the specified name has artist. /// diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs index b4519d02de..c4a79c22d0 100644 --- a/MediaBrowser.Dlna/DlnaManager.cs +++ b/MediaBrowser.Dlna/DlnaManager.cs @@ -541,7 +541,6 @@ namespace MediaBrowser.Dlna public void Dispose() { - throw new NotImplementedException(); } } } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs b/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs index 4344ceb0a5..75195c8c51 100644 --- a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs +++ b/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs @@ -34,7 +34,7 @@ namespace MediaBrowser.Server.Implementations.Connect { LoadCachedAddress(); - _timer = new Timer(TimerCallback, null, TimeSpan.FromSeconds(30), TimeSpan.FromHours(12)); + _timer = new Timer(TimerCallback, null, TimeSpan.FromSeconds(10), TimeSpan.FromHours(12)); } private async void TimerCallback(object state) diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 2f2973130b..7c336f6d7a 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Common.Configuration; +using System.IO; +using System.Text; +using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -79,21 +81,42 @@ namespace MediaBrowser.XbmcMetadata.Parsers { using (var streamReader = BaseNfoSaver.GetStreamReader(metadataFile)) { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) + // Need to handle a url after the xml data + // http://kodi.wiki/view/NFO_files/movies + + var xml = streamReader.ReadToEnd(); + + var index = xml.LastIndexOf('>'); + + if (index != -1) + { + xml = xml.Substring(0, index + 1); + } + + using (var ms = new MemoryStream()) { - reader.MoveToContent(); + var bytes = Encoding.UTF8.GetBytes(xml); - // Loop through each element - while (reader.Read()) + ms.Write(bytes, 0, bytes.Length); + ms.Position = 0; + + // Use XmlReader for best performance + using (var reader = XmlReader.Create(ms, settings)) { - cancellationToken.ThrowIfCancellationRequested(); + reader.MoveToContent(); - if (reader.NodeType == XmlNodeType.Element) + // Loop through each element + while (reader.Read()) { - FetchDataFromXmlNode(reader, item, userDataList); + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) + { + FetchDataFromXmlNode(reader, item, userDataList); + } } } + } } } From 931bf29c3e64870dc4187064b91ec58461d6ac38 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 21 Oct 2014 08:42:02 -0400 Subject: [PATCH 4/9] update web client packager --- .../Localization/JavaScript/javascript.json | 3 ++- .../Localization/Server/server.json | 4 +++- MediaBrowser.WebDashboard/Api/DashboardService.cs | 5 +++-- MediaBrowser.WebDashboard/Api/PackageCreator.cs | 2 ++ .../MediaBrowser.WebDashboard.csproj | 9 +++++++++ 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json index 0611114e3a..929c3143d3 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json @@ -74,7 +74,8 @@ "ButtonMarkTheseRead": "Mark these read", "ButtonClose": "Close", "LabelAllPlaysSentToPlayer": "All plays will be sent to the selected player.", - "MessageInvalidUser": "Invalid user or password.", + "MessageInvalidUser": "Invalid username or password. Please try again.", + "HeaderLoginFailure": "Login Failure", "HeaderAllRecordings": "All Recordings", "RecommendationBecauseYouLike": "Because you like {0}", "RecommendationBecauseYouWatched": "Because you watched {0}", diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index 575b38c530..59a7d4b64e 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -1252,5 +1252,7 @@ "HeaderTrailerReel": "Trailer Reel", "OptionPlayUnwatchedTrailersOnly": "Play only unwatched trailers", "HeaderTrailerReelHelp": "Start a trailer reel to play a long running playlist of trailers.", - "MessageNoTrailersFound": "No trailers found. Install the Trailer channel plugin to import a library of internet trailers." + "MessageNoTrailersFound": "No trailers found. Install the Trailer channel plugin to import a library of internet trailers.", + "HeaderNewUsers": "New Users", + "ButtonSignUp": "Sign up" } diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 85a3888474..4919b9e6a1 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Extensions; +using System.Globalization; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller; @@ -286,7 +287,7 @@ namespace MediaBrowser.WebDashboard.Api var culture = "en-US"; - var appVersion = _appHost.ApplicationVersion.ToString(); + var appVersion = DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture); await DumpHtml(creator.DashboardUIPath, path, culture, appVersion); await DumpJs(creator.DashboardUIPath, path, culture, appVersion); diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index c1316223e0..f4ae854257 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -246,6 +246,7 @@ namespace MediaBrowser.WebDashboard.Api await AppendResource(memoryStream, "thirdparty/jquery.unveil-custom.js", newLineBytes).ConfigureAwait(false); await AppendResource(memoryStream, "thirdparty/cast_sender.js", newLineBytes).ConfigureAwait(false); + await AppendResource(memoryStream, "thirdparty/md5.js", newLineBytes).ConfigureAwait(false); await AppendLocalization(memoryStream, culture).ConfigureAwait(false); await memoryStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false); @@ -344,6 +345,7 @@ namespace MediaBrowser.WebDashboard.Api "channelslatest.js", "channelitems.js", "channelsettings.js", + "connectlogin.js", "dashboardgeneral.js", "dashboardpage.js", "dashboardsync.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 719b1abd81..a50d3aae29 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -104,6 +104,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -695,6 +698,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -1535,6 +1541,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest From 1e7ac871db62d749725226fe05d61ab2629e79e9 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 22 Oct 2014 00:42:08 -0400 Subject: [PATCH 5/9] add http client timeout --- .../HttpClientManager/HttpClientManager.cs | 48 +++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs index 4a8597b7fc..093b904f1f 100644 --- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs +++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs @@ -123,7 +123,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager } request.Method = method; - request.Timeout = 20000; + request.Timeout = options.TimeoutMs; if (!string.IsNullOrEmpty(options.Host)) { @@ -390,7 +390,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager if (!options.BufferContent) { - var response = await httpWebRequest.GetResponseAsync().ConfigureAwait(false); + var response = await GetResponseAsync(httpWebRequest, TimeSpan.FromMilliseconds(options.TimeoutMs)).ConfigureAwait(false); var httpResponse = (HttpWebResponse)response; @@ -401,7 +401,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager return GetResponseInfo(httpResponse, httpResponse.GetResponseStream(), GetContentLength(httpResponse), httpResponse); } - using (var response = await httpWebRequest.GetResponseAsync().ConfigureAwait(false)) + using (var response = await GetResponseAsync(httpWebRequest, TimeSpan.FromMilliseconds(options.TimeoutMs)).ConfigureAwait(false)) { var httpResponse = (HttpWebResponse)response; @@ -843,5 +843,47 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager { return Post(url, postData, null, cancellationToken); } + + private Task GetResponseAsync(WebRequest request, TimeSpan timeout) + { + var taskCompletion = new TaskCompletionSource(); + + Task asyncTask = Task.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, null); + + ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, TimeoutCallback, request, timeout, true); + asyncTask.ContinueWith(task => + { + taskCompletion.TrySetResult(task.Result); + + }, TaskContinuationOptions.NotOnFaulted); + + // Handle errors + asyncTask.ContinueWith(task => + { + if (task.Exception != null) + { + taskCompletion.TrySetException(task.Exception); + } + else + { + taskCompletion.TrySetException(new List()); + } + + }, TaskContinuationOptions.OnlyOnFaulted); + + return taskCompletion.Task; + } + + private static void TimeoutCallback(object state, bool timedOut) + { + if (timedOut) + { + WebRequest request = (WebRequest)state; + if (state != null) + { + request.Abort(); + } + } + } } } From 9f3891d418729b9cc1dbdf4e48013f5a0a57639a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 22 Oct 2014 00:42:26 -0400 Subject: [PATCH 6/9] render movies as folders with dlna --- .../Playback/BaseStreamingService.cs | 6 +- .../Playback/Hls/DynamicHlsService.cs | 12 +- .../Playback/Hls/VideoHlsService.cs | 2 +- MediaBrowser.Common/Net/HttpRequestOptions.cs | 4 + .../Library/IMetadataSaver.cs | 1 - .../ContentDirectory/ControlHandler.cs | 143 +- MediaBrowser.Dlna/Didl/DidlBuilder.cs | 115 +- MediaBrowser.Dlna/DlnaManager.cs | 3 +- MediaBrowser.Dlna/MediaBrowser.Dlna.csproj | 4 + MediaBrowser.Dlna/PlayTo/PlayToController.cs | 53 +- .../Profiles/PopcornHourProfile.cs | 178 ++ .../Profiles/SamsungSmartTvProfile.cs | 9 +- .../Profiles/SonyBlurayPlayer2013Profile.cs | 2 +- .../Profiles/SonyBravia2010Profile.cs | 2 + .../Profiles/SonyBravia2011Profile.cs | 1 + .../Profiles/SonyBravia2012Profile.cs | 1 + .../Profiles/SonyBravia2013Profile.cs | 1 + MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs | 65 +- MediaBrowser.Dlna/Profiles/XboxOneProfile.cs | 39 +- MediaBrowser.Dlna/Profiles/Xml/Android.xml | 1 + MediaBrowser.Dlna/Profiles/Xml/Default.xml | 1 + MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml | 1 + .../Profiles/Xml/DirecTV HD-DVR.xml | 1 + .../Profiles/Xml/Dish Hopper-Joey.xml | 1 + .../Profiles/Xml/LG Smart TV.xml | 1 + .../Profiles/Xml/Linksys DMA2100.xml | 1 + .../Profiles/Xml/MediaMonkey.xml | 1 + .../Profiles/Xml/Panasonic Viera.xml | 1 + .../Profiles/Xml/Popcorn Hour.xml | 74 + .../Profiles/Xml/Samsung Smart TV.xml | 2 +- .../Profiles/Xml/Sony Blu-ray Player 2013.xml | 1 + .../Profiles/Xml/Sony Blu-ray Player.xml | 1 + .../Profiles/Xml/Sony Bravia (2010).xml | 1 + .../Profiles/Xml/Sony Bravia (2011).xml | 1 + .../Profiles/Xml/Sony Bravia (2012).xml | 1 + .../Profiles/Xml/Sony Bravia (2013).xml | 1 + .../Profiles/Xml/Sony PlayStation 3.xml | 15 +- MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml | 1 + .../Profiles/Xml/Windows 8 RT.xml | 1 + .../Profiles/Xml/Windows Phone.xml | 1 + MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml | 1 + MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml | 20 +- MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml | 1 + MediaBrowser.Dlna/Ssdp/SsdpHandler.cs | 1 + MediaBrowser.Model/Dlna/DeviceProfile.cs | 2 + .../Connect/ConnectEntryPoint.cs | 2 +- .../Localization/JavaScript/ar.json | 7 +- .../Localization/JavaScript/ca.json | 7 +- .../Localization/JavaScript/cs.json | 5 +- .../Localization/JavaScript/da.json | 7 +- .../Localization/JavaScript/de.json | 7 +- .../Localization/JavaScript/el.json | 7 +- .../Localization/JavaScript/en_GB.json | 7 +- .../Localization/JavaScript/en_US.json | 7 +- .../Localization/JavaScript/es.json | 7 +- .../Localization/JavaScript/es_MX.json | 9 +- .../Localization/JavaScript/fr.json | 7 +- .../Localization/JavaScript/he.json | 7 +- .../Localization/JavaScript/hr.json | 7 +- .../Localization/JavaScript/it.json | 21 +- .../Localization/JavaScript/kk.json | 7 +- .../Localization/JavaScript/ms.json | 7 +- .../Localization/JavaScript/nb.json | 7 +- .../Localization/JavaScript/nl.json | 19 +- .../Localization/JavaScript/pl.json | 7 +- .../Localization/JavaScript/pt_BR.json | 7 +- .../Localization/JavaScript/pt_PT.json | 7 +- .../Localization/JavaScript/ru.json | 15 +- .../Localization/JavaScript/sv.json | 37 +- .../Localization/JavaScript/tr.json | 7 +- .../Localization/JavaScript/vi.json | 7 +- .../Localization/JavaScript/zh_CN.json | 131 +- .../Localization/JavaScript/zh_TW.json | 7 +- .../Localization/Server/ar.json | 471 ++--- .../Localization/Server/ca.json | 471 ++--- .../Localization/Server/cs.json | 471 ++--- .../Localization/Server/da.json | 471 ++--- .../Localization/Server/de.json | 471 ++--- .../Localization/Server/el.json | 471 ++--- .../Localization/Server/en_GB.json | 471 ++--- .../Localization/Server/en_US.json | 471 ++--- .../Localization/Server/es.json | 471 ++--- .../Localization/Server/es_MX.json | 471 ++--- .../Localization/Server/fr.json | 471 ++--- .../Localization/Server/he.json | 471 ++--- .../Localization/Server/hr.json | 471 ++--- .../Localization/Server/it.json | 479 ++--- .../Localization/Server/kk.json | 471 ++--- .../Localization/Server/ko.json | 471 ++--- .../Localization/Server/ms.json | 471 ++--- .../Localization/Server/nb.json | 471 ++--- .../Localization/Server/nl.json | 473 ++--- .../Localization/Server/pl.json | 471 ++--- .../Localization/Server/pt_BR.json | 471 ++--- .../Localization/Server/pt_PT.json | 471 ++--- .../Localization/Server/ru.json | 473 ++--- .../Localization/Server/server.json | 3 +- .../Localization/Server/sv.json | 485 ++--- .../Localization/Server/tr.json | 471 ++--- .../Localization/Server/vi.json | 471 ++--- .../Localization/Server/zh_CN.json | 1677 +++++++++-------- .../Localization/Server/zh_TW.json | 471 ++--- .../Savers/AlbumNfoSaver.cs | 2 +- .../Savers/ArtistNfoSaver.cs | 2 +- .../Savers/BaseNfoSaver.cs | 7 +- .../Savers/EpisodeNfoSaver.cs | 2 +- .../Savers/MovieNfoSaver.cs | 2 +- .../Savers/SeasonNfoSaver.cs | 2 +- .../Savers/SeriesNfoSaver.cs | 2 +- 109 files changed, 8247 insertions(+), 7349 deletions(-) create mode 100644 MediaBrowser.Dlna/Profiles/PopcornHourProfile.cs create mode 100644 MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index ec6ca7737b..7289ac0862 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -714,8 +714,10 @@ namespace MediaBrowser.Api.Playback /// true if the specified stream is H264; otherwise, false. protected bool IsH264(MediaStream stream) { - return stream.Codec.IndexOf("264", StringComparison.OrdinalIgnoreCase) != -1 || - stream.Codec.IndexOf("avc", StringComparison.OrdinalIgnoreCase) != -1; + var codec = stream.Codec ?? string.Empty; + + return codec.IndexOf("264", StringComparison.OrdinalIgnoreCase) != -1 || + codec.IndexOf("avc", StringComparison.OrdinalIgnoreCase) != -1; } /// diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index bea4ba37ed..30ba25ae47 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -367,16 +367,6 @@ namespace MediaBrowser.Api.Playback.Hls { var state = await GetState(request, CancellationToken.None).ConfigureAwait(false); - if (string.Equals(request.AudioCodec, "copy", StringComparison.OrdinalIgnoreCase)) - { - throw new ArgumentException("Audio codec copy is not allowed here."); - } - - if (string.Equals(request.VideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) - { - throw new ArgumentException("Video codec copy is not allowed here."); - } - if (string.IsNullOrEmpty(request.MediaSourceId)) { throw new ArgumentException("MediaSourceId is required"); @@ -638,7 +628,7 @@ namespace MediaBrowser.Api.Playback.Hls // See if we can save come cpu cycles by avoiding encoding if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase)) { - return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy"; + return state.VideoStream != null && IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy"; } var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 02acf7e718..06fa4065c2 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -139,7 +139,7 @@ namespace MediaBrowser.Api.Playback.Hls // See if we can save come cpu cycles by avoiding encoding if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) { - return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy"; + return state.VideoStream != null && IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy"; } var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs index 09bf6e2328..81f1d70d39 100644 --- a/MediaBrowser.Common/Net/HttpRequestOptions.cs +++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs @@ -94,6 +94,8 @@ namespace MediaBrowser.Common.Net public CacheMode CacheMode { get; set; } public TimeSpan CacheLength { get; set; } + public int TimeoutMs { get; set; } + private string GetHeaderValue(string name) { string value; @@ -115,6 +117,8 @@ namespace MediaBrowser.Common.Net LogRequest = true; CacheMode = CacheMode.None; + + TimeoutMs = 20000; } public void SetPostData(IDictionary values) diff --git a/MediaBrowser.Controller/Library/IMetadataSaver.cs b/MediaBrowser.Controller/Library/IMetadataSaver.cs index ce8feb4c6c..f13cf45879 100644 --- a/MediaBrowser.Controller/Library/IMetadataSaver.cs +++ b/MediaBrowser.Controller/Library/IMetadataSaver.cs @@ -1,5 +1,4 @@ using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; using System.Threading; namespace MediaBrowser.Controller.Library diff --git a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs index 3f9ce66c5c..b92a4174ef 100644 --- a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs +++ b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs @@ -1,7 +1,9 @@ -using MediaBrowser.Common.Extensions; +using System.Linq; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; using MediaBrowser.Dlna.Didl; @@ -85,7 +87,9 @@ namespace MediaBrowser.Dlna.ContentDirectory { var id = sparams["ObjectID"]; - var item = GetItemFromObjectId(id, user); + var serverItem = GetItemFromObjectId(id, user); + + var item = serverItem.Item; var newbookmark = int.Parse(sparams["PosSecond"], _usCulture); @@ -173,49 +177,48 @@ namespace MediaBrowser.Dlna.ContentDirectory //didl.SetAttribute("xmlns:sec", NS_SEC); result.AppendChild(didl); - var item = GetItemFromObjectId(id, user); + var serverItem = GetItemFromObjectId(id, user); + var item = serverItem.Item; var totalCount = 0; if (string.Equals(flag, "BrowseMetadata")) { - var folder = item as Folder; - - if (folder == null) + if (item.IsFolder || serverItem.StubType.HasValue) { - result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, item, null, deviceId, filter)); + var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requested).ConfigureAwait(false)); + totalCount = childrenResult.TotalRecordCount; + + result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, item, serverItem.StubType, null, totalCount, filter, id)); } else { - var childrenResult = (await GetUserItems(folder, user, sortCriteria, start, requested).ConfigureAwait(false)); - totalCount = childrenResult.TotalRecordCount; - - result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, folder, totalCount, filter, id)); + result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, item, null, null, deviceId, filter)); } + provided++; } else { - var folder = (Folder)item; - - var childrenResult = (await GetUserItems(folder, user, sortCriteria, start, requested).ConfigureAwait(false)); + var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requested).ConfigureAwait(false)); totalCount = childrenResult.TotalRecordCount; provided = childrenResult.Items.Length; foreach (var i in childrenResult.Items) { - if (i.IsFolder) + var displayStubType = GetDisplayStubType(i, serverItem.Item); + + if (i.IsFolder || displayStubType.HasValue) { - var f = (Folder)i; - var childCount = (await GetUserItems(f, user, sortCriteria, null, 0).ConfigureAwait(false)) + var childCount = (await GetUserItems(i, displayStubType, user, sortCriteria, null, 0).ConfigureAwait(false)) .TotalRecordCount; - result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, f, childCount, filter)); + result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, i, displayStubType, item, childCount, filter)); } else { - result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, i, folder, deviceId, filter)); + result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, i, item, serverItem.StubType, deviceId, filter)); } } } @@ -231,6 +234,24 @@ namespace MediaBrowser.Dlna.ContentDirectory }; } + private StubType? GetDisplayStubType(BaseItem item, BaseItem context) + { + if (context == null || context.IsFolder) + { + var movie = item as Movie; + if (movie != null) + { + if (movie.LocalTrailerIds.Count > 0 || + movie.SpecialFeatureIds.Count > 0) + { + return StubType.Folder; + } + } + } + + return null; + } + private async Task>> HandleSearch(Headers sparams, User user, string deviceId) { var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", "")); @@ -269,9 +290,11 @@ namespace MediaBrowser.Dlna.ContentDirectory result.AppendChild(didl); - var folder = (Folder)GetItemFromObjectId(sparams["ContainerID"], user); + var serverItem = GetItemFromObjectId(sparams["ContainerID"], user); + + var item = serverItem.Item; - var childrenResult = (await GetChildrenSorted(folder, user, searchCriteria, sortCriteria, start, requested).ConfigureAwait(false)); + var childrenResult = (await GetChildrenSorted(item, user, searchCriteria, sortCriteria, start, requested).ConfigureAwait(false)); var totalCount = childrenResult.TotalRecordCount; @@ -281,15 +304,14 @@ namespace MediaBrowser.Dlna.ContentDirectory { if (i.IsFolder) { - var f = (Folder)i; - var childCount = (await GetChildrenSorted(f, user, searchCriteria, sortCriteria, null, 0).ConfigureAwait(false)) + var childCount = (await GetChildrenSorted(i, user, searchCriteria, sortCriteria, null, 0).ConfigureAwait(false)) .TotalRecordCount; - result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, f, childCount, filter)); + result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, i, null, item, childCount, filter)); } else { - result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, i, folder, deviceId, filter)); + result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, i, item, serverItem.StubType, deviceId, filter)); } } @@ -304,8 +326,10 @@ namespace MediaBrowser.Dlna.ContentDirectory }; } - private async Task> GetChildrenSorted(Folder folder, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit) + private async Task> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit) { + var folder = (Folder)item; + var sortOrders = new List(); if (!folder.IsPreSorted) { @@ -340,7 +364,7 @@ namespace MediaBrowser.Dlna.ContentDirectory //items = items.OfType(); isFolder = true; } - + return await folder.GetItems(new InternalItemsQuery { Limit = limit, @@ -356,8 +380,20 @@ namespace MediaBrowser.Dlna.ContentDirectory }).ConfigureAwait(false); } - private async Task> GetUserItems(Folder folder, User user, SortCriteria sort, int? startIndex, int? limit) + private async Task> GetUserItems(BaseItem item, StubType? stubType, User user, SortCriteria sort, int? startIndex, int? limit) { + if (stubType.HasValue) + { + var movie = item as Movie; + + if (movie != null) + { + return await GetMovieItems(movie).ConfigureAwait(false); + } + } + + var folder = (Folder)item; + var sortOrders = new List(); if (!folder.IsPreSorted) { @@ -376,6 +412,23 @@ namespace MediaBrowser.Dlna.ContentDirectory }).ConfigureAwait(false); } + private Task> GetMovieItems(Movie item) + { + var list = new List(); + + list.Add(item); + + list.AddRange(item.LocalTrailerIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null)); + list.AddRange(item.SpecialFeatureIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null)); + list.AddRange(item.ThemeVideoIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null)); + + return Task.FromResult(new QueryResult + { + Items = list.ToArray(), + TotalRecordCount = list.Count + }); + } + private bool FilterUnsupportedContent(BaseItem i, User user) { // Unplayable @@ -399,26 +452,50 @@ namespace MediaBrowser.Dlna.ContentDirectory return true; } - private BaseItem GetItemFromObjectId(string id, User user) + private ServerItem GetItemFromObjectId(string id, User user) { return DidlBuilder.IsIdRoot(id) - ? user.RootFolder + ? new ServerItem { Item = user.RootFolder } : ParseItemId(id, user); } - private BaseItem ParseItemId(string id, User user) + private ServerItem ParseItemId(string id, User user) { Guid itemId; + StubType? stubType = null; + + if (id.StartsWith("folder_", StringComparison.OrdinalIgnoreCase)) + { + stubType = StubType.Folder; + id = id.Split(new[] { '_' }, 2)[1]; + } if (Guid.TryParse(id, out itemId)) { - return _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId); + + return new ServerItem + { + Item = item, + StubType = stubType + }; } Logger.Error("Error parsing item Id: {0}. Returning user root folder.", id); - return user.RootFolder; + return new ServerItem { Item = user.RootFolder }; } } + + internal class ServerItem + { + public BaseItem Item { get; set; } + public StubType? StubType { get; set; } + } + + public enum StubType + { + Folder = 0 + } } diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index 6bc50b1ea9..912612d299 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -9,6 +9,7 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Playlists; +using MediaBrowser.Dlna.ContentDirectory; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; @@ -63,25 +64,31 @@ namespace MediaBrowser.Dlna.Didl result.AppendChild(didl); - result.DocumentElement.AppendChild(GetItemElement(result, item, context, deviceId, filter, streamInfo)); + result.DocumentElement.AppendChild(GetItemElement(result, item, context, null, deviceId, filter, streamInfo)); return result.DocumentElement.OuterXml; } - public XmlElement GetItemElement(XmlDocument doc, BaseItem item, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo = null) + public XmlElement GetItemElement(XmlDocument doc, BaseItem item, BaseItem context, StubType? contextStubType, string deviceId, Filter filter, StreamInfo streamInfo = null) { + var clientId = GetClientId(item, null); + var element = doc.CreateElement(string.Empty, "item", NS_DIDL); element.SetAttribute("restricted", "1"); - element.SetAttribute("id", item.Id.ToString("N")); + element.SetAttribute("id", clientId); - if (item.Parent != null) + if (context != null) + { + element.SetAttribute("parentID", GetClientId(context, contextStubType)); + } + else if (item.Parent != null) { - element.SetAttribute("parentID", item.Parent.Id.ToString("N")); + element.SetAttribute("parentID", GetClientId(item.Parent, null)); } //AddBookmarkInfo(item, user, element); - AddGeneralProperties(item, context, element, filter); + AddGeneralProperties(item, null, context, element, filter); // refID? // storeAttribute(itemNode, object, ClassProperties.REF_ID, false); @@ -111,14 +118,14 @@ namespace MediaBrowser.Dlna.Didl { var sources = _user == null ? video.GetMediaSources(true).ToList() : video.GetMediaSources(true, _user).ToList(); - streamInfo = new StreamBuilder().BuildVideoItem(new VideoOptions - { - ItemId = video.Id.ToString("N"), - MediaSources = sources, - Profile = _profile, - DeviceId = deviceId, - MaxBitrate = _profile.MaxStreamingBitrate - }); + streamInfo = new StreamBuilder().BuildVideoItem(new VideoOptions + { + ItemId = GetClientId(video), + MediaSources = sources, + Profile = _profile, + DeviceId = deviceId, + MaxBitrate = _profile.MaxStreamingBitrate + }); } var targetWidth = streamInfo.TargetWidth; @@ -311,7 +318,7 @@ namespace MediaBrowser.Dlna.Didl return item.Name; } - + private void AddAudioResource(XmlElement container, IHasMediaSources audio, string deviceId, Filter filter, StreamInfo streamInfo = null) { var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL); @@ -322,7 +329,7 @@ namespace MediaBrowser.Dlna.Didl streamInfo = new StreamBuilder().BuildAudioItem(new AudioOptions { - ItemId = audio.Id.ToString("N"), + ItemId = GetClientId(audio), MediaSources = sources, Profile = _profile, DeviceId = deviceId @@ -403,8 +410,8 @@ namespace MediaBrowser.Dlna.Didl public static bool IsIdRoot(string id) { - if (string.IsNullOrWhiteSpace(id) || - + if (string.IsNullOrWhiteSpace(id) || + string.Equals(id, "0", StringComparison.OrdinalIgnoreCase) // Samsung sometimes uses 1 as root @@ -416,13 +423,15 @@ namespace MediaBrowser.Dlna.Didl return false; } - public XmlElement GetFolderElement(XmlDocument doc, BaseItem folder, int childCount, Filter filter, string requestedId = null) + public XmlElement GetFolderElement(XmlDocument doc, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null) { var container = doc.CreateElement(string.Empty, "container", NS_DIDL); container.SetAttribute("restricted", "0"); container.SetAttribute("searchable", "1"); container.SetAttribute("childCount", childCount.ToString(_usCulture)); + var clientId = GetClientId(folder, stubType); + if (string.Equals(requestedId, "0")) { container.SetAttribute("id", "0"); @@ -430,20 +439,20 @@ namespace MediaBrowser.Dlna.Didl } else { - container.SetAttribute("id", folder.Id.ToString("N")); + container.SetAttribute("id", clientId); - var parent = folder.Parent; + var parent = context ?? folder.Parent; if (parent == null) { container.SetAttribute("parentID", "0"); } else { - container.SetAttribute("parentID", parent.Id.ToString("N")); + container.SetAttribute("parentID", GetClientId(parent, null)); } } - AddCommonFields(folder, null, container, filter); + AddCommonFields(folder, stubType, null, container, filter); AddCover(folder, container); @@ -466,10 +475,11 @@ namespace MediaBrowser.Dlna.Didl /// Adds fields used by both items and folders /// /// The item. + /// Type of the item stub. /// The context. /// The element. /// The filter. - private void AddCommonFields(BaseItem item, BaseItem context, XmlElement element, Filter filter) + private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem context, XmlElement element, Filter filter) { // Don't filter on dc:title because not all devices will include it in the filter // MediaMonkey for example won't display content without a title @@ -478,7 +488,7 @@ namespace MediaBrowser.Dlna.Didl AddValue(element, "dc", "title", GetDisplayName(item, context), NS_DC); } - element.AppendChild(CreateObjectClass(element.OwnerDocument, item)); + element.AppendChild(CreateObjectClass(element.OwnerDocument, item, itemStubType)); if (filter.Contains("dc:date")) { @@ -539,14 +549,14 @@ namespace MediaBrowser.Dlna.Didl AddPeople(item, element); } - private XmlElement CreateObjectClass(XmlDocument result, BaseItem item) + private XmlElement CreateObjectClass(XmlDocument result, BaseItem item, StubType? stubType) { // More types here // http://oss.linn.co.uk/repos/Public/LibUpnpCil/DidlLite/UpnpAv/Test/TestDidlLite.cs var objectClass = result.CreateElement("upnp", "class", NS_UPNP); - if (item.IsFolder) + if (item.IsFolder || stubType.HasValue) { string classType = null; @@ -560,7 +570,7 @@ namespace MediaBrowser.Dlna.Didl { classType = "object.container.person.musicArtist"; } - else if (item is Series || item is Season || item is BoxSet) + else if (item is Series || item is Season || item is BoxSet || item is Video) { classType = "object.container.album.videoAlbum"; } @@ -628,9 +638,9 @@ namespace MediaBrowser.Dlna.Didl } } - private void AddGeneralProperties(BaseItem item, BaseItem context, XmlElement element, Filter filter) + private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlElement element, Filter filter) { - AddCommonFields(item, context, element, filter); + AddCommonFields(item, itemStubType, context, element, filter); var audio = item as Audio; @@ -773,20 +783,26 @@ namespace MediaBrowser.Dlna.Didl } } - AddImageResElement(item, element, 4096, 4096, playbackPercentage, "jpg", "JPEG_LRG"); - AddImageResElement(item, element, 4096, 4096, playbackPercentage, "png", "PNG_LRG"); - AddImageResElement(item, element, 1024, 768, playbackPercentage, "jpg", "JPEG_MED"); - AddImageResElement(item, element, 640, 480, playbackPercentage, "jpg", "JPEG_SM"); + var imageLimit = _profile.DidlAlbumArtLimit ?? 100; + AddImageResElement(item, element, 160, 160, playbackPercentage, "jpg", "JPEG_TN"); - AddImageResElement(item, element, 160, 160, playbackPercentage, "png", "PNG_TN"); + + if (imageLimit > 1) + { + AddImageResElement(item, element, 4096, 4096, playbackPercentage, "jpg", "JPEG_LRG"); + AddImageResElement(item, element, 1024, 768, playbackPercentage, "jpg", "JPEG_MED"); + AddImageResElement(item, element, 640, 480, playbackPercentage, "jpg", "JPEG_SM"); + AddImageResElement(item, element, 4096, 4096, playbackPercentage, "png", "PNG_LRG"); + AddImageResElement(item, element, 160, 160, playbackPercentage, "png", "PNG_TN"); + } } - private void AddImageResElement(BaseItem item, - XmlElement element, - int maxWidth, - int maxHeight, + private void AddImageResElement(BaseItem item, + XmlElement element, + int maxWidth, + int maxHeight, int playbackPercentage, - string format, + string format, string org_Pn) { var imageInfo = GetImageInfo(item); @@ -920,6 +936,25 @@ namespace MediaBrowser.Dlna.Didl internal int? Height; } + public static string GetClientId(BaseItem item, StubType? stubType) + { + var id = item.Id.ToString("N"); + + if (stubType.HasValue) + { + id = stubType.Value.ToString().ToLower() + "_" + id; + } + + return id; + } + + public static string GetClientId(IHasMediaSources item) + { + var id = item.Id.ToString("N"); + + return id; + } + private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, int playbackPercentage, string format) { var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/{7}", diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs index c4a79c22d0..f4578eca7c 100644 --- a/MediaBrowser.Dlna/DlnaManager.cs +++ b/MediaBrowser.Dlna/DlnaManager.cs @@ -528,7 +528,8 @@ namespace MediaBrowser.Dlna new AndroidProfile(), new DirectTvProfile(), new DishHopperJoeyProfile(), - new DefaultProfile() + new DefaultProfile(), + new PopcornHourProfile() }; foreach (var item in list) diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj index 761ce52c88..bc6e4f32a6 100644 --- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj +++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj @@ -75,6 +75,7 @@ + @@ -194,6 +195,9 @@ + + +