diff options
Diffstat (limited to 'MediaBrowser.WebDashboard/Api/DashboardService.cs')
| -rw-r--r-- | MediaBrowser.WebDashboard/Api/DashboardService.cs | 545 |
1 files changed, 62 insertions, 483 deletions
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index a9765889f..6e3439079 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; @@ -7,6 +8,7 @@ using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; using ServiceStack; using ServiceStack.Web; @@ -14,10 +16,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 +48,12 @@ namespace MediaBrowser.WebDashboard.Api public string Name { get; set; } } + [Route("/web/Package", "GET")] + [Route("/dashboard/Package", "GET")] + public class GetDashboardPackage + { + } + /// <summary> /// Class GetDashboardResource /// </summary> @@ -121,35 +126,6 @@ namespace MediaBrowser.WebDashboard.Api } /// <summary> - /// Gets the dashboard UI path. - /// </summary> - /// <value>The dashboard UI path.</value> - 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"); - } - } - - /// <summary> - /// Gets the dashboard resource path. - /// </summary> - /// <param name="virtualPath">The virtual path.</param> - /// <returns>System.String.</returns> - private string GetDashboardResourcePath(string virtualPath) - { - return Path.Combine(DashboardUIPath, virtualPath.Replace('/', Path.DirectorySeparatorChar)); - } - - /// <summary> /// Gets the specified request. /// </summary> /// <param name="request">The request.</param> @@ -158,7 +134,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)); } /// <summary> @@ -228,10 +204,10 @@ 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); + path = path.Replace("scripts/jquery.mobile-1.4.5.min.map", "thirdparty/jquerymobile-1.4.5/jquery.mobile-1.4.5.min.map", StringComparison.OrdinalIgnoreCase); var localizationCulture = GetLocalizationCulture(); @@ -241,7 +217,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 +233,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 +245,17 @@ namespace MediaBrowser.WebDashboard.Api /// Gets the resource stream. /// </summary> /// <param name="path">The path.</param> - /// <param name="isHtml">if set to <c>true</c> [is HTML].</param> /// <param name="localizationCulture">The localization culture.</param> /// <returns>Task{Stream}.</returns> - private async Task<Stream> GetResourceStream(string path, bool isHtml, string localizationCulture) + private Task<Stream> 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()); } - /// <summary> - /// Gets the raw resource stream. - /// </summary> - /// <param name="path">The path.</param> - /// <returns>Task{Stream}.</returns> - 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); } /// <summary> @@ -322,448 +268,81 @@ namespace MediaBrowser.WebDashboard.Api return Path.GetExtension(path).EndsWith("html", StringComparison.OrdinalIgnoreCase); } - /// <summary> - /// Modifies the HTML by adding common meta tags, css and js. - /// </summary> - /// <param name="sourceStream">The source stream.</param> - /// <param name="userId">The user identifier.</param> - /// <param name="localizationCulture">The localization culture.</param> - /// <returns>Task{Stream}.</returns> - private async Task<Stream> ModifyHtml(Stream sourceStream, string localizationCulture) + public async Task<object> Get(GetDashboardPackage request) { - using (sourceStream) - { - string html; + var path = Path.Combine(_serverConfigurationManager.ApplicationPaths.ProgramDataPath, + "webclient-dump"); - 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("<html>", "<html lang=\"" + lang + "\">"); - } - - //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("<head>", "<head>" + GetMetaTags() + GetCommonCss(version) + GetCommonJavascript(version)); - - var bytes = Encoding.UTF8.GetBytes(html); - - return new MemoryStream(bytes); + try + { + Directory.Delete(path, true); } - } - - private string GetLocalizationToken(string phrase) - { - return "${" + phrase + "}"; - } - - /// <summary> - /// Gets the meta tags. - /// </summary> - /// <returns>System.String.</returns> - private static string GetMetaTags() - { - var sb = new StringBuilder(); - - sb.Append("<meta http-equiv=\"X-UA-Compatibility\" content=\"IE=Edge\">"); - sb.Append("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\">"); - //sb.Append("<meta name=\"apple-mobile-web-app-capable\" content=\"yes\">"); - sb.Append("<meta name=\"mobile-web-app-capable\" content=\"yes\">"); - sb.Append("<meta name=\"application-name\" content=\"Media Browser\">"); - //sb.Append("<meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black-translucent\">"); - - sb.Append("<link rel=\"icon\" sizes=\"114x114\" href=\"css/images/touchicon114.png\" />"); - - // http://developer.apple.com/library/ios/#DOCUMENTATION/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html - sb.Append("<link rel=\"apple-touch-icon\" href=\"css/images/touchicon.png\" />"); - sb.Append("<link rel=\"apple-touch-icon\" sizes=\"72x72\" href=\"css/images/touchicon72.png\" />"); - sb.Append("<link rel=\"apple-touch-icon\" sizes=\"114x114\" href=\"css/images/touchicon114.png\" />"); - sb.Append("<link rel=\"apple-touch-startup-image\" href=\"css/images/iossplash.png\" />"); - sb.Append("<link rel=\"shortcut icon\" href=\"css/images/favicon.ico\" />"); - - return sb.ToString(); - } - - /// <summary> - /// Gets the common CSS. - /// </summary> - /// <param name="version">The version.</param> - /// <returns>System.String.</returns> - 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", - "css/all.css" + versionString - }; - - var tags = files.Select(s => string.Format("<link rel=\"stylesheet\" href=\"{0}\" />", s)).ToArray(); + catch (IOException) + { - return string.Join(string.Empty, tags); - } + } - /// <summary> - /// Gets the common javascript. - /// </summary> - /// <param name="version">The version.</param> - /// <returns>System.String.</returns> - private static string GetCommonJavascript(Version version) - { - var builder = new StringBuilder(); + var creator = GetPackageCreator(); - var versionString = "?v=" + version; + CopyDirectory(creator.DashboardUIPath, path); - var files = new[] - { - "scripts/all.js" + versionString, - "thirdparty/jstree1.0/jquery.jstree.min.js" - }; + var culture = "en-US"; - var tags = files.Select(s => string.Format("<script src=\"{0}\"></script>", s)).ToArray(); + var appVersion = DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture); - builder.Append(string.Join(string.Empty, tags)); + await DumpHtml(creator.DashboardUIPath, path, culture, appVersion); + await DumpJs(creator.DashboardUIPath, path, culture, appVersion); - return builder.ToString(); + 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 ""; } - /// <summary> - /// Gets a stream containing all concatenated javascript - /// </summary> - /// <returns>Task{Stream}.</returns> - private async Task<Stream> GetAllJavascript() + private async Task DumpHtml(string source, string destination, string culture, string appVersion) { - 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) + foreach (var file in Directory.GetFiles(source, "*.html", SearchOption.TopDirectoryOnly)) { - 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(); + var filename = Path.GetFileName(file); - using (var fs = _fileSystem.GetFileStream(GetDashboardResourcePath("thirdparty/mediabrowser.apiclient.js"), 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); - } + await DumpFile(filename, Path.Combine(destination, filename), culture, appVersion).ConfigureAwait(false); } - - 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<string> 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", - "directorybrowser.js", - "dlnaprofile.js", - "dlnaprofiles.js", - "dlnasettings.js", - "dlnaserversettings.js", - "editcollectionitems.js", - "edititemmetadata.js", - "edititemimages.js", - "edititemsubtitles.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", - "metadataxbmc.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", - "userimagepage.js", - "userprofilespage.js", - "userparentalcontrol.js", - "wizardfinishpage.js", - "wizardservice.js", - "wizardstartpage.js", - "wizardsettings.js", - "wizarduserpage.js" - }; } - private async Task AppendLocalization(Stream stream) + private async Task DumpJs(string source, string destination, string culture, string appVersion) { - var js = "window.localizationGlossary=" + _jsonSerializer.SerializeToString(_localization.GetJavaScriptLocalizationDictionary(GetLocalizationCulture())); + foreach (var file in Directory.GetFiles(source, "*.js", SearchOption.TopDirectoryOnly)) + { + var filename = Path.GetFileName(file); - var bytes = Encoding.UTF8.GetBytes(js); - await stream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); + await DumpFile("scripts/" + filename, Path.Combine(destination, "scripts", filename), culture, appVersion).ConfigureAwait(false); + } } - /// <summary> - /// Gets all CSS. - /// </summary> - /// <returns>Task{Stream}.</returns> - private async Task<Stream> GetAllCss() + private async Task DumpFile(string resourceVirtualPath, string destinationFilePath, string culture, string appVersion) { - 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(resourceVirtualPath, 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(destinationFilePath, 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; } - /// <summary> - /// Appends the resource. - /// </summary> - /// <param name="outputStream">The output stream.</param> - /// <param name="path">The path.</param> - /// <param name="newLineBytes">The new line bytes.</param> - /// <returns>Task.</returns> - 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); } } |
