add localization stub

This commit is contained in:
Luke Pulverenti 2014-03-30 21:00:47 -04:00
parent 59a4d3c953
commit 5a014b093c
18 changed files with 537 additions and 212 deletions

View File

@ -31,6 +31,14 @@ namespace MediaBrowser.Api
{
}
/// <summary>
/// Class ParentalRatings
/// </summary>
[Route("/Localization/Options", "GET", Summary = "Gets localization options")]
public class GetLocalizationOptions : IReturn<List<LocalizatonOption>>
{
}
/// <summary>
/// Class CulturesService
/// </summary>
@ -62,6 +70,13 @@ namespace MediaBrowser.Api
return ToOptimizedSerializedResultUsingCache(result);
}
public object Get(GetLocalizationOptions request)
{
var result = _localization.GetLocalizationOptions().ToList();
return ToOptimizedSerializedResultUsingCache(result);
}
/// <summary>
/// Gets the specified request.
/// </summary>

View File

@ -276,7 +276,7 @@ namespace MediaBrowser.Api.Playback.Hls
? 0
: ((GetHlsVideoStream)state.VideoRequest).TimeStampOffsetMs;
var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds);
var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture));
var threads = GetNumberOfThreads(state, false);

View File

@ -1,7 +1,7 @@
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Localization
{
@ -31,5 +31,35 @@ namespace MediaBrowser.Controller.Localization
/// <param name="rating">The rating.</param>
/// <returns>System.Int32.</returns>
int? GetRatingLevel(string rating);
/// <summary>
/// Gets the localized string.
/// </summary>
/// <param name="phrase">The phrase.</param>
/// <param name="culture">The culture.</param>
/// <returns>System.String.</returns>
string GetLocalizedString(string phrase, string culture);
/// <summary>
/// Gets the localized string.
/// </summary>
/// <param name="phrase">The phrase.</param>
/// <returns>System.String.</returns>
string GetLocalizedString(string phrase);
/// <summary>
/// Localizes the document.
/// </summary>
/// <param name="document">The document.</param>
/// <param name="culture">The culture.</param>
/// <param name="tokenBuilder">The token builder.</param>
/// <returns>System.String.</returns>
string LocalizeDocument(string document, string culture, Func<string, string> tokenBuilder);
/// <summary>
/// Gets the localization options.
/// </summary>
/// <returns>IEnumerable{LocalizatonOption}.</returns>
IEnumerable<LocalizatonOption> GetLocalizationOptions();
}
}

View File

@ -218,6 +218,8 @@ namespace MediaBrowser.Model.Configuration
public string ServerName { get; set; }
public string WanDdns { get; set; }
public string UICulture { get; set; }
public DlnaOptions DlnaOptions { get; set; }
/// <summary>
@ -281,6 +283,8 @@ namespace MediaBrowser.Model.Configuration
MetadataOptions = options.ToArray();
DlnaOptions = new DlnaOptions();
UICulture = "en-us";
}
}

View File

@ -30,4 +30,10 @@ namespace MediaBrowser.Model.Globalization
/// <value>The name of the three letter ISO region.</value>
public string ThreeLetterISORegionName { get; set; }
}
public class LocalizatonOption
{
public string Name { get; set; }
public string Value { get; set; }
}
}

View File

@ -0,0 +1,3 @@
{
"LabelTest": "Text"
}

View File

@ -1,9 +1,9 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Serialization;
using MoreLinq;
using System;
using System.Collections.Concurrent;
@ -11,6 +11,8 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using MediaBrowser.Common.Extensions;
namespace MediaBrowser.Server.Implementations.Localization
{
@ -33,15 +35,17 @@ namespace MediaBrowser.Server.Implementations.Localization
new ConcurrentDictionary<string, Dictionary<string, ParentalRating>>(StringComparer.OrdinalIgnoreCase);
private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer;
/// <summary>
/// Initializes a new instance of the <see cref="LocalizationManager"/> class.
/// </summary>
/// <param name="configurationManager">The configuration manager.</param>
public LocalizationManager(IServerConfigurationManager configurationManager, IFileSystem fileSystem)
public LocalizationManager(IServerConfigurationManager configurationManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer)
{
_configurationManager = configurationManager;
_fileSystem = fileSystem;
_jsonSerializer = jsonSerializer;
ExtractAll();
}
@ -241,5 +245,108 @@ namespace MediaBrowser.Server.Implementations.Localization
return value == null ? (int?)null : value.Value;
}
public string GetLocalizedString(string phrase)
{
return GetLocalizedString(phrase, _configurationManager.Configuration.UICulture);
}
public string GetLocalizedString(string phrase, string culture)
{
var dictionary = GetLocalizationDictionary(culture);
string value;
if (dictionary.TryGetValue(phrase, out value))
{
return value;
}
return phrase;
}
private readonly ConcurrentDictionary<string, Dictionary<string, string>> _dictionaries =
new ConcurrentDictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
public Dictionary<string, string> GetLocalizationDictionary(string culture)
{
const string prefix = "Server";
var key = prefix + culture;
return _dictionaries.GetOrAdd(key, k => GetDictionary(prefix, culture, "server.json"));
}
public Dictionary<string, string> GetJavaScriptLocalizationDictionary(string culture)
{
const string prefix = "JavaScript";
var key = prefix + culture;
return _dictionaries.GetOrAdd(key, k => GetDictionary(prefix, culture, "javascript.json"));
}
private Dictionary<string, string> GetDictionary(string prefix, string culture, string baseFilename)
{
var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var assembly = GetType().Assembly;
var namespaceName = GetType().Namespace + "." + prefix;
CopyInto(dictionary, namespaceName + "." + baseFilename, assembly);
CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture), assembly);
return dictionary;
}
private void CopyInto(IDictionary<string, string> dictionary, string resourcePath, Assembly assembly)
{
using (var stream = assembly.GetManifestResourceStream(resourcePath))
{
if (stream != null)
{
var dict = _jsonSerializer.DeserializeFromStream<Dictionary<string, string>>(stream);
foreach (var key in dict.Keys)
{
dictionary[key] = dict[key];
}
}
}
}
private string GetResourceFilename(string culture)
{
var parts = culture.Split('-');
if (parts.Length == 2)
{
culture = parts[0].ToLower() + "_" + parts[1].ToUpper();
}
else
{
culture = culture.ToLower();
}
return culture + ".json";
}
public IEnumerable<LocalizatonOption> GetLocalizationOptions()
{
return new List<LocalizatonOption>
{
new LocalizatonOption{ Name="English (United States)", Value="en-us"}
};
}
public string LocalizeDocument(string document, string culture, Func<string,string> tokenBuilder)
{
foreach (var pair in GetLocalizationDictionary(culture).ToList())
{
var token = tokenBuilder(pair.Key);
document = document.Replace(token, pair.Value, StringComparison.Ordinal);
}
return document;
}
}
}

View File

@ -0,0 +1,17 @@
{
"LabelExit": "Exit",
"LabelVisitCommunity": "Visit Community",
"LabelGithubWiki": "Github Wiki",
"LabelSwagger": "Swagger",
"LabelStandard": "Standard",
"LabelViewApiDocumentation": "View Api Documentation",
"LabelBrowseLibrary": "Browse Library",
"LabelConfigureMediaBrowser": "Configure Media Browser",
"LabelOpenLibraryViewer": "Open Library Viewer",
"LabelRestartServer": "Restart Server",
"LabelShowLogWindow": "Show Log Window",
"LabelPrevious": "Previous",
"LabelFinish": "Finish",
"LabelNext": "Next",
"LabelYoureDone": "You're Done!"
}

View File

@ -282,6 +282,8 @@
<EmbeddedResource Include="Localization\Ratings\ru.txt" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Localization\JavaScript\javascript.json" />
<EmbeddedResource Include="Localization\Server\server.json" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
@ -386,6 +388,7 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" Condition=" '$(ConfigurationName)' != 'Release Mono' " />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@ -170,7 +170,7 @@ namespace MediaBrowser.ServerApplication
private ILiveTvManager LiveTvManager { get; set; }
private ILocalizationManager LocalizationManager { get; set; }
internal ILocalizationManager LocalizationManager { get; set; }
private IEncodingManager EncodingManager { get; set; }
private IChannelManager ChannelManager { get; set; }
@ -421,6 +421,9 @@ namespace MediaBrowser.ServerApplication
RegisterSingleInstance(ServerConfigurationManager);
LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager, JsonSerializer);
RegisterSingleInstance(LocalizationManager);
RegisterSingleInstance<IWebSocketServer>(() => new AlchemyServer(Logger));
RegisterSingleInstance<IBlurayExaminer>(() => new BdInfoExaminer());
@ -472,9 +475,6 @@ namespace MediaBrowser.ServerApplication
ServerManager = new ServerManager(this, JsonSerializer, LogManager.GetLogger("ServerManager"), ServerConfigurationManager);
RegisterSingleInstance(ServerManager);
LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager);
RegisterSingleInstance(LocalizationManager);
var innerProgress = new ActionableProgress<double>();
innerProgress.RegisterAction(p => progress.Report((.75 * p) + 15));

View File

@ -16,18 +16,16 @@ namespace MediaBrowser.ServerApplication
{
private readonly IJsonSerializer _jsonSerializer;
private readonly ILibraryManager _libraryManager;
private readonly IDisplayPreferencesRepository _displayPreferencesManager;
private readonly IItemRepository _itemRepository;
private User _currentUser;
public LibraryViewer(IJsonSerializer jsonSerializer, IUserManager userManager, ILibraryManager libraryManager, IDisplayPreferencesRepository displayPreferencesManager, IItemRepository itemRepo)
public LibraryViewer(IJsonSerializer jsonSerializer, IUserManager userManager, ILibraryManager libraryManager, IItemRepository itemRepo)
{
InitializeComponent();
_jsonSerializer = jsonSerializer;
_libraryManager = libraryManager;
_displayPreferencesManager = displayPreferencesManager;
_itemRepository = itemRepo;
foreach (var user in userManager.Users)
@ -44,7 +42,7 @@ namespace MediaBrowser.ServerApplication
if (e.Node != null)
{
var item = (BaseItem)e.Node.Tag;
lblType.Text = "Type: " + item.GetType().Name;
lblType.Text = item.GetType().Name;
var json = FormatJson(_jsonSerializer.SerializeToString(item));

View File

@ -253,7 +253,7 @@ namespace MediaBrowser.ServerApplication
{
//Application.EnableVisualStyles();
//Application.SetCompatibleTextRenderingDefault(false);
_serverNotifyIcon = new ServerNotifyIcon(_appHost.LogManager, _appHost, _appHost.ServerConfigurationManager, _appHost.UserManager, _appHost.LibraryManager, _appHost.JsonSerializer, _appHost.DisplayPreferencesRepository, _appHost.ItemRepository);
_serverNotifyIcon = new ServerNotifyIcon(_appHost.LogManager, _appHost, _appHost.ServerConfigurationManager, _appHost.UserManager, _appHost.LibraryManager, _appHost.JsonSerializer, _appHost.DisplayPreferencesRepository, _appHost.ItemRepository, _appHost.LocalizationManager);
Application.Run();
}

View File

@ -4,6 +4,7 @@ using System.Windows.Forms;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
@ -41,6 +42,7 @@ namespace MediaBrowser.ServerApplication
private readonly IJsonSerializer _jsonSerializer;
private readonly IDisplayPreferencesRepository _displayPreferencesManager;
private readonly IItemRepository _itemRepository;
private readonly ILocalizationManager _localization;
private LogForm _logForm;
public bool Visible
@ -56,10 +58,17 @@ namespace MediaBrowser.ServerApplication
}
}
public ServerNotifyIcon(ILogManager logManager, IServerApplicationHost appHost, IServerConfigurationManager configurationManager, IUserManager userManager, ILibraryManager libraryManager, IJsonSerializer jsonSerializer, IDisplayPreferencesRepository displayPreferencesManager, IItemRepository itemRepo)
public ServerNotifyIcon(ILogManager logManager,
IServerApplicationHost appHost,
IServerConfigurationManager configurationManager,
IUserManager userManager, ILibraryManager libraryManager,
IJsonSerializer jsonSerializer,
IDisplayPreferencesRepository displayPreferencesManager,
IItemRepository itemRepo, ILocalizationManager localization)
{
_logger = logManager.GetLogger("MainWindow");
_itemRepository = itemRepo;
_localization = localization;
_appHost = appHost;
_logManager = logManager;
_configurationManager = configurationManager;
@ -118,20 +127,17 @@ namespace MediaBrowser.ServerApplication
//
cmdExit.Name = "cmdExit";
cmdExit.Size = new System.Drawing.Size(208, 22);
cmdExit.Text = "Exit";
//
// cmdCommunity
//
cmdCommunity.Name = "cmdCommunity";
cmdCommunity.Size = new System.Drawing.Size(208, 22);
cmdCommunity.Text = "Visit Community";
//
// cmdLogWindow
//
cmdLogWindow.CheckOnClick = true;
cmdLogWindow.Name = "cmdLogWindow";
cmdLogWindow.Size = new System.Drawing.Size(208, 22);
cmdLogWindow.Text = "Show Log Window";
//
// toolStripSeparator1
//
@ -142,13 +148,11 @@ namespace MediaBrowser.ServerApplication
//
cmdRestart.Name = "cmdRestart";
cmdRestart.Size = new System.Drawing.Size(208, 22);
cmdRestart.Text = "Restart Server";
//
// cmdLibraryExplorer
//
cmdLibraryExplorer.Name = "cmdLibraryExplorer";
cmdLibraryExplorer.Size = new System.Drawing.Size(208, 22);
cmdLibraryExplorer.Text = "Open Library Explorer";
//
// toolStripSeparator2
//
@ -159,13 +163,11 @@ namespace MediaBrowser.ServerApplication
//
cmdConfigure.Name = "cmdConfigure";
cmdConfigure.Size = new System.Drawing.Size(208, 22);
cmdConfigure.Text = "Configure Media Browser";
//
// cmdBrowse
//
cmdBrowse.Name = "cmdBrowse";
cmdBrowse.Size = new System.Drawing.Size(208, 22);
cmdBrowse.Text = "Browse Library";
//
// cmdApiDocs
//
@ -175,25 +177,21 @@ namespace MediaBrowser.ServerApplication
cmdGtihub});
cmdApiDocs.Name = "cmdApiDocs";
cmdApiDocs.Size = new System.Drawing.Size(208, 22);
cmdApiDocs.Text = "View Api Documentation";
//
// cmdStandardDocs
//
cmdStandardDocs.Name = "cmdStandardDocs";
cmdStandardDocs.Size = new System.Drawing.Size(136, 22);
cmdStandardDocs.Text = "Standard";
//
// cmdSwagger
//
cmdSwagger.Name = "cmdSwagger";
cmdSwagger.Size = new System.Drawing.Size(136, 22);
cmdSwagger.Text = "Swagger";
//
// cmdGtihub
//
cmdGtihub.Name = "cmdGtihub";
cmdGtihub.Size = new System.Drawing.Size(136, 22);
cmdGtihub.Text = "Github Wiki";
cmdExit.Click += cmdExit_Click;
cmdRestart.Click += cmdRestart_Click;
@ -211,6 +209,8 @@ namespace MediaBrowser.ServerApplication
_logManager.LoggerLoaded += LoadLogWindow;
_configurationManager.ConfigurationUpdated += Instance_ConfigurationUpdated;
LocalizeText();
if (_appHost.IsFirstRun)
{
Action action = () => notifyIcon1.ShowBalloonTip(5000, "Media Browser", "Welcome to Media Browser Server!", ToolTipIcon.Info);
@ -219,6 +219,24 @@ namespace MediaBrowser.ServerApplication
}
}
private void LocalizeText()
{
_uiCulture = _configurationManager.Configuration.UICulture;
cmdExit.Text = _localization.GetLocalizedString("LabelExit");
cmdCommunity.Text = _localization.GetLocalizedString("LabelVisitCommunity");
cmdGtihub.Text = _localization.GetLocalizedString("LabelGithubWiki");
cmdSwagger.Text = _localization.GetLocalizedString("LabelSwagger");
cmdStandardDocs.Text = _localization.GetLocalizedString("LabelStandard");
cmdApiDocs.Text = _localization.GetLocalizedString("LabelViewApiDocumentation");
cmdBrowse.Text = _localization.GetLocalizedString("LabelBrowseLibrary");
cmdConfigure.Text = _localization.GetLocalizedString("LabelConfigureMediaBrowser");
cmdLibraryExplorer.Text = _localization.GetLocalizedString("LabelOpenLibraryViewer");
cmdRestart.Text = _localization.GetLocalizedString("LabelRestartServer");
cmdLogWindow.Text = _localization.GetLocalizedString("LabelShowLogWindow");
}
private string _uiCulture;
/// <summary>
/// Handles the ConfigurationUpdated event of the Instance control.
/// </summary>
@ -226,6 +244,12 @@ namespace MediaBrowser.ServerApplication
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
void Instance_ConfigurationUpdated(object sender, EventArgs e)
{
if (!string.Equals(_configurationManager.Configuration.UICulture, _uiCulture,
StringComparison.OrdinalIgnoreCase))
{
LocalizeText();
}
Action action = () =>
{
var isLogWindowOpen = _logForm != null;
@ -307,7 +331,7 @@ namespace MediaBrowser.ServerApplication
void cmdLibraryExplorer_Click(object sender, EventArgs e)
{
new LibraryViewer(_jsonSerializer, _userManager, _libraryManager, _displayPreferencesManager, _itemRepository).Show();
new LibraryViewer(_jsonSerializer, _userManager, _libraryManager, _itemRepository).Show();
}
void cmdRestart_Click(object sender, EventArgs e)

View File

@ -121,9 +121,9 @@
this.lblStatus.Location = new System.Drawing.Point(3, 0);
this.lblStatus.MaximumSize = new System.Drawing.Size(0, 100);
this.lblStatus.Name = "lblStatus";
this.lblStatus.Size = new System.Drawing.Size(599, 59);
this.lblStatus.Size = new System.Drawing.Size(469, 59);
this.lblStatus.TabIndex = 0;
this.lblStatus.Text = "Loading Media Browser Server";
this.lblStatus.Text = "Loading Media Browser";
this.lblStatus.UseWaitCursor = true;
//
// panel1

View File

@ -3,6 +3,7 @@ using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging;
@ -15,6 +16,8 @@ using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using WebMarkupMin.Core.Minifiers;
using WebMarkupMin.Core.Settings;
namespace MediaBrowser.WebDashboard.Api
{
@ -96,6 +99,7 @@ namespace MediaBrowser.WebDashboard.Api
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
/// <summary>
/// Initializes a new instance of the <see cref="DashboardService" /> class.
@ -103,11 +107,12 @@ namespace MediaBrowser.WebDashboard.Api
/// <param name="appHost">The app host.</param>
/// <param name="serverConfigurationManager">The server configuration manager.</param>
/// <param name="fileSystem">The file system.</param>
public DashboardService(IServerApplicationHost appHost, IServerConfigurationManager serverConfigurationManager, IFileSystem fileSystem)
public DashboardService(IServerApplicationHost appHost, IServerConfigurationManager serverConfigurationManager, IFileSystem fileSystem, ILocalizationManager localization)
{
_appHost = appHost;
_serverConfigurationManager = serverConfigurationManager;
_fileSystem = fileSystem;
_localization = localization;
}
/// <summary>
@ -148,7 +153,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(), page.Plugin.AssemblyDateLastModified, null, MimeTypes.GetMimeType("page.html"), () => ModifyHtml(page.GetHtmlStream()));
return ResultFactory.GetStaticResult(Request, page.Plugin.Version.ToString().GetMD5(), page.Plugin.AssemblyDateLastModified, null, MimeTypes.GetMimeType("page.html"), () => ModifyHtml(page.GetHtmlStream(), null));
}
/// <summary>
@ -210,13 +215,16 @@ namespace MediaBrowser.WebDashboard.Api
var contentType = MimeTypes.GetMimeType(path);
var isHtml = IsHtml(path);
var localizationCulture = isHtml ? GetLocalizationCulture() : null;
// Don't cache if not configured to do so
// But always cache images to simulate production
if (!_serverConfigurationManager.Configuration.EnableDashboardResponseCaching &&
!contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) &&
!contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase))
{
return ResultFactory.GetResult(GetResourceStream(path).Result, contentType);
return ResultFactory.GetResult(GetResourceStream(path, isHtml, localizationCulture).Result, contentType);
}
TimeSpan? cacheDuration = null;
@ -230,17 +238,24 @@ namespace MediaBrowser.WebDashboard.Api
var assembly = GetType().Assembly.GetName();
var cacheKey = (assembly.Version + path).GetMD5();
var cacheKey = (assembly.Version + (localizationCulture ?? string.Empty) + path).GetMD5();
return ResultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(path));
return ResultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(path, isHtml, localizationCulture));
}
private string GetLocalizationCulture()
{
return _serverConfigurationManager.Configuration.UICulture;
}
/// <summary>
/// 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)
private async Task<Stream> GetResourceStream(string path, bool isHtml, string localizationCulture)
{
Stream resourceStream;
@ -259,13 +274,11 @@ namespace MediaBrowser.WebDashboard.Api
if (resourceStream != null)
{
var isHtml = IsHtml(path);
// 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).ConfigureAwait(false);
resourceStream = await ModifyHtml(resourceStream, localizationCulture).ConfigureAwait(false);
}
}
@ -297,26 +310,48 @@ namespace MediaBrowser.WebDashboard.Api
/// </summary>
/// <param name="sourceStream">The source stream.</param>
/// <returns>Task{Stream}.</returns>
internal async Task<Stream> ModifyHtml(Stream sourceStream)
private async Task<Stream> ModifyHtml(Stream sourceStream, string localizationCulture)
{
string html;
using (var memoryStream = new MemoryStream())
using (sourceStream)
{
await sourceStream.CopyToAsync(memoryStream).ConfigureAwait(false);
string html;
html = Encoding.UTF8.GetString(memoryStream.ToArray());
using (var memoryStream = new MemoryStream())
{
await sourceStream.CopyToAsync(memoryStream).ConfigureAwait(false);
html = Encoding.UTF8.GetString(memoryStream.ToArray());
if (!string.IsNullOrWhiteSpace(localizationCulture))
{
html = _localization.LocalizeDocument(html, localizationCulture, GetLocalizationToken);
}
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);
}
}
var version = GetType().Assembly.GetName().Version;
html = html.Replace("<head>", "<head>" + GetMetaTags() + GetCommonCss(version) + GetCommonJavascript(version));
var bytes = Encoding.UTF8.GetBytes(html);
sourceStream.Dispose();
return new MemoryStream(bytes);
private string GetLocalizationToken(string phrase)
{
return "${" + phrase + "}";
}
/// <summary>
@ -393,154 +428,200 @@ namespace MediaBrowser.WebDashboard.Api
/// <returns>Task{Stream}.</returns>
private async Task<Stream> GetAllJavascript()
{
var scriptFiles = new[]
{
"extensions.js",
"site.js",
"librarybrowser.js",
"librarylist.js",
"editorsidebar.js",
"librarymenu.js",
"chromecast.js",
"contextmenu.js",
"mediacontroller.js",
"mediaplayer.js",
"mediaplayer-video.js",
"ratingdialog.js",
"aboutpage.js",
"allusersettings.js",
"alphapicker.js",
"addpluginpage.js",
"advancedconfigurationpage.js",
"advancedpaths.js",
"advancedserversettings.js",
"metadataadvanced.js",
"appsplayback.js",
"appsweather.js",
"autoorganizetv.js",
"autoorganizelog.js",
"channels.js",
"channelitems.js",
"dashboardinfo.js",
"dashboardpage.js",
"directorybrowser.js",
"dlnaprofile.js",
"dlnaprofiles.js",
"dlnasettings.js",
"editcollectionitems.js",
"edititemmetadata.js",
"edititempeople.js",
"edititemimages.js",
"encodingsettings.js",
"gamesrecommendedpage.js",
"gamesystemspage.js",
"gamespage.js",
"gamegenrepage.js",
"gamestudiospage.js",
"indexpage.js",
"itembynamedetailpage.js",
"itemdetailpage.js",
"itemgallery.js",
"itemlistpage.js",
"librarypathmapping.js",
"libraryreport.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",
"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",
"notifications.js",
"playlist.js",
"plugincatalogpage.js",
"pluginspage.js",
"pluginupdatespage.js",
"remotecontrol.js",
"scheduledtaskpage.js",
"scheduledtaskspage.js",
"search.js",
"songs.js",
"supporterkeypage.js",
"supporterpage.js",
"episodes.js",
"tvgenres.js",
"tvlatest.js",
"tvpeople.js",
"tvrecommended.js",
"tvshows.js",
"tvstudios.js",
"tvupcoming.js",
"useredit.js",
"userpassword.js",
"userimagepage.js",
"userprofilespage.js",
"usersettings.js",
"userparentalcontrol.js",
"wizardfinishpage.js",
"wizardimagesettings.js",
"wizardservice.js",
"wizardstartpage.js",
"wizardsettings.js",
"wizarduserpage.js"
};
var memoryStream = new MemoryStream();
var newLineBytes = Encoding.UTF8.GetBytes(Environment.NewLine);
// jQuery + jQuery mobile
await AppendResource(memoryStream, "thirdparty/jquery-2.0.3.min.js", newLineBytes).ConfigureAwait(false);
await AppendResource(memoryStream, "thirdparty/jquerymobile-1.4.2/jquery.mobile-1.4.2.min.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);
await AppendResource(memoryStream, "thirdparty/autonumeric/autoNumeric.min.js", newLineBytes).ConfigureAwait(false);
var builder = new StringBuilder();
var assembly = GetType().Assembly;
await AppendResource(assembly, memoryStream, "MediaBrowser.WebDashboard.ApiClient.js", newLineBytes).ConfigureAwait(false);
foreach (var file in scriptFiles)
using (var stream = assembly.GetManifestResourceStream("MediaBrowser.WebDashboard.ApiClient.js"))
{
await AppendResource(memoryStream, "scripts/" + file, newLineBytes).ConfigureAwait(false);
using (var streamReader = new StreamReader(stream))
{
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<string> GetScriptFiles()
{
return new[]
{
"extensions.js",
"site.js",
"librarybrowser.js",
"librarylist.js",
"editorsidebar.js",
"librarymenu.js",
"chromecast.js",
"contextmenu.js",
"mediacontroller.js",
"mediaplayer.js",
"mediaplayer-video.js",
"ratingdialog.js",
"aboutpage.js",
"allusersettings.js",
"alphapicker.js",
"addpluginpage.js",
"advancedconfigurationpage.js",
"advancedpaths.js",
"advancedserversettings.js",
"metadataadvanced.js",
"appsplayback.js",
"appsweather.js",
"autoorganizetv.js",
"autoorganizelog.js",
"channels.js",
"channelitems.js",
"dashboardinfo.js",
"dashboardpage.js",
"directorybrowser.js",
"dlnaprofile.js",
"dlnaprofiles.js",
"dlnasettings.js",
"editcollectionitems.js",
"edititemmetadata.js",
"edititempeople.js",
"edititemimages.js",
"encodingsettings.js",
"gamesrecommendedpage.js",
"gamesystemspage.js",
"gamespage.js",
"gamegenrepage.js",
"gamestudiospage.js",
"indexpage.js",
"itembynamedetailpage.js",
"itemdetailpage.js",
"itemgallery.js",
"itemlistpage.js",
"librarypathmapping.js",
"libraryreport.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",
"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",
"notifications.js",
"playlist.js",
"plugincatalogpage.js",
"pluginspage.js",
"pluginupdatespage.js",
"remotecontrol.js",
"scheduledtaskpage.js",
"scheduledtaskspage.js",
"search.js",
"songs.js",
"supporterkeypage.js",
"supporterpage.js",
"episodes.js",
"tvgenres.js",
"tvlatest.js",
"tvpeople.js",
"tvrecommended.js",
"tvshows.js",
"tvstudios.js",
"tvupcoming.js",
"useredit.js",
"userpassword.js",
"userimagepage.js",
"userprofilespage.js",
"usersettings.js",
"userparentalcontrol.js",
"wizardfinishpage.js",
"wizardimagesettings.js",
"wizardservice.js",
"wizardstartpage.js",
"wizardsettings.js",
"wizarduserpage.js"
};
}
private async Task AppendLocalization(Stream stream)
{
}
/// <summary>
/// Gets all CSS.
/// </summary>
@ -568,37 +649,42 @@ namespace MediaBrowser.WebDashboard.Api
"icons.css"
};
var memoryStream = new MemoryStream();
var newLineBytes = Encoding.UTF8.GetBytes(Environment.NewLine);
var builder = new StringBuilder();
foreach (var file in files)
{
await AppendResource(memoryStream, "css/" + file, newLineBytes).ConfigureAwait(false);
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;
}
/// <summary>
/// Appends the resource.
/// </summary>
/// <param name="assembly">The assembly.</param>
/// <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(Assembly assembly, Stream outputStream, string path, byte[] newLineBytes)
{
using (var stream = assembly.GetManifestResourceStream(path))
{
await stream.CopyToAsync(outputStream).ConfigureAwait(false);
await outputStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false);
}
}
/// <summary>
/// Appends the resource.
/// </summary>

View File

@ -57,6 +57,9 @@
<Reference Include="ServiceStack.Interfaces">
<HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
</Reference>
<Reference Include="WebMarkupMin.Core">
<HintPath>..\packages\WebMarkupMin.Core.0.8.18\lib\net40\WebMarkupMin.Core.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\SharedVersion.cs">
@ -641,9 +644,6 @@
<Content Include="dashboard-ui\livetvseriestimers.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\thirdparty\autonumeric\autoNumeric.min.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\thirdparty\jquery-2.0.3.min.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@ -1477,9 +1477,6 @@
<Content Include="dashboard-ui\scripts\tvstudios.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\thirdparty\autonumeric\autoNumeric.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\thirdparty\jstree1.0\jquery.jstree.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@ -1970,6 +1967,7 @@
</Content>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="dashboard-ui\css\fonts\OpenSans-ExtraBold.woff">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
@ -1983,6 +1981,9 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
<None Include="WebMarkupMin.Configuration.xsd">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="webMarkupMin">
<section name="core" type="WebMarkupMin.Core.Configuration.CoreConfiguration, WebMarkupMin.Core" />
</sectionGroup>
</configSections>
<webMarkupMin xmlns="http://tempuri.org/WebMarkupMin.Configuration.xsd">
<core>
<css>
<minifiers>
<add name="NullCssMinifier" displayName="Null CSS Minifier" type="WebMarkupMin.Core.Minifiers.NullCssMinifier, WebMarkupMin.Core" />
<add name="KristensenCssMinifier" displayName="Mads Kristensen's CSS minifier" type="WebMarkupMin.Core.Minifiers.KristensenCssMinifier, WebMarkupMin.Core" />
</minifiers>
</css>
<js>
<minifiers>
<add name="NullJsMinifier" displayName="Null JS Minifier" type="WebMarkupMin.Core.Minifiers.NullJsMinifier, WebMarkupMin.Core" />
<add name="CrockfordJsMinifier" displayName="Douglas Crockford's JS Minifier" type="WebMarkupMin.Core.Minifiers.CrockfordJsMinifier, WebMarkupMin.Core" />
</minifiers>
</js>
<logging>
<loggers>
<add name="NullLogger" displayName="Null Logger" type="WebMarkupMin.Core.Loggers.NullLogger, WebMarkupMin.Core" />
<add name="ThrowExceptionLogger" displayName="Throw exception logger" type="WebMarkupMin.Core.Loggers.ThrowExceptionLogger, WebMarkupMin.Core" />
</loggers>
</logging>
</core>
</webMarkupMin>
</configuration>

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.249" targetFramework="net45" />
<package id="WebMarkupMin.Core" version="0.8.18" targetFramework="net45" />
</packages>