Added the ability to reload the server and created a Plugins solution

This commit is contained in:
LukePulverenti Luke Pulverenti luke pulverenti 2012-09-17 14:09:35 -04:00
parent f1770605ea
commit fb88e4d5fc
27 changed files with 707 additions and 68 deletions

View File

@ -5,7 +5,7 @@ using System.ComponentModel.Composition;
namespace MediaBrowser.Api
{
[Export(typeof(BasePlugin))]
public class Plugin : BaseGenericPlugin<BasePluginConfiguration>
public class Plugin : BasePlugin
{
public override string Name
{

View File

@ -67,13 +67,27 @@ namespace MediaBrowser.Common.Kernel
/// </summary>
public abstract KernelContext KernelContext { get; }
protected BaseKernel()
/// <summary>
/// Initializes the Kernel
/// </summary>
public async Task Init(IProgress<TaskProgress> progress)
{
ApplicationPaths = new TApplicationPathsType();
// Performs initializations that only occur once
InitializeInternal(progress);
// Performs initializations that can be reloaded at anytime
await Reload(progress).ConfigureAwait(false);
progress.Report(new TaskProgress { Description = "Loading Complete", PercentComplete = 100 });
}
public virtual async Task Init(IProgress<TaskProgress> progress)
/// <summary>
/// Performs initializations that only occur once
/// </summary>
protected virtual void InitializeInternal(IProgress<TaskProgress> progress)
{
ApplicationPaths = new TApplicationPathsType();
ReloadLogger();
progress.Report(new TaskProgress { Description = "Loading configuration", PercentComplete = 0 });
@ -81,11 +95,24 @@ namespace MediaBrowser.Common.Kernel
progress.Report(new TaskProgress { Description = "Starting Http server", PercentComplete = 5 });
ReloadHttpServer();
progress.Report(new TaskProgress { Description = "Loading Plugins", PercentComplete = 10 });
await ReloadComposableParts().ConfigureAwait(false);
}
/// <summary>
/// Performs initializations that can be reloaded at anytime
/// </summary>
public virtual async Task Reload(IProgress<TaskProgress> progress)
{
await Task.Run(() =>
{
progress.Report(new TaskProgress { Description = "Loading Plugins", PercentComplete = 10 });
ReloadComposableParts();
}).ConfigureAwait(false);
}
/// <summary>
/// Disposes the current logger and creates a new one
/// </summary>
private void ReloadLogger()
{
DisposeLogger();
@ -104,9 +131,7 @@ namespace MediaBrowser.Common.Kernel
/// Uses MEF to locate plugins
/// Subclasses can use this to locate types within plugins
/// </summary>
protected virtual Task ReloadComposableParts()
{
return Task.Run(() =>
private void ReloadComposableParts()
{
DisposeComposableParts();
@ -118,9 +143,11 @@ namespace MediaBrowser.Common.Kernel
container.Catalog.Dispose();
container.Dispose();
});
}
/// <summary>
/// Constructs an MEF CompositionContainer based on the current running assembly and all plugin assemblies
/// </summary>
public CompositionContainer GetCompositionContainer(bool includeCurrentAssembly = false)
{
// Gets all plugin assemblies by first reading all bytes of the .dll and calling Assembly.Load against that
@ -147,25 +174,17 @@ namespace MediaBrowser.Common.Kernel
/// </summary>
protected virtual void OnComposablePartsLoaded()
{
StartPlugins();
}
/// <summary>
/// Initializes all plugins
/// </summary>
private void StartPlugins()
{
// Start-up each plugin
foreach (BasePlugin plugin in Plugins)
{
plugin.Initialize(this);
}
}
/// <summary>
/// Reloads application configuration from the config file
/// </summary>
protected virtual void ReloadConfiguration()
private void ReloadConfiguration()
{
//Configuration information for anything other than server-specific configuration will have to come via the API... -ebr
@ -213,8 +232,12 @@ namespace MediaBrowser.Common.Kernel
/// </summary>
public virtual void Dispose()
{
Logger.LogInfo("Beginning Kernel.Dispose");
DisposeComposableParts();
DisposeHttpServer();
DisposeLogger();
}
@ -233,6 +256,8 @@ namespace MediaBrowser.Common.Kernel
{
if (Plugins != null)
{
Logger.LogInfo("Disposing Plugins");
foreach (BasePlugin plugin in Plugins)
{
plugin.Dispose();
@ -247,6 +272,8 @@ namespace MediaBrowser.Common.Kernel
{
if (HttpServer != null)
{
Logger.LogInfo("Disposing Http Server");
HttpServer.Dispose();
}
@ -265,6 +292,8 @@ namespace MediaBrowser.Common.Kernel
if (Logger.LoggerInstance != null)
{
Logger.LogInfo("Disposing Logger");
Logger.LoggerInstance.Dispose();
}
}
@ -292,6 +321,7 @@ namespace MediaBrowser.Common.Kernel
KernelContext KernelContext { get; }
Task Init(IProgress<TaskProgress> progress);
Task Reload(IProgress<TaskProgress> progress);
void Dispose();
}
}

View File

@ -86,6 +86,7 @@
<Compile Include="Logging\TraceLogger.cs" />
<Compile Include="Net\Handlers\StaticFileHandler.cs" />
<Compile Include="Net\MimeTypes.cs" />
<Compile Include="Plugins\BaseTheme.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>

View File

@ -6,30 +6,6 @@ using System.IO;
namespace MediaBrowser.Common.Plugins
{
/// <summary>
/// Provides a BasePlugin with generics, allowing for strongly typed configuration access.
/// </summary>
public abstract class BaseGenericPlugin<TConfigurationType> : BasePlugin
where TConfigurationType : BasePluginConfiguration, new()
{
public new TConfigurationType Configuration
{
get
{
return base.Configuration as TConfigurationType;
}
set
{
base.Configuration = value;
}
}
public override Type ConfigurationType
{
get { return typeof(TConfigurationType); }
}
}
/// <summary>
/// Provides a common base class for all plugins
/// </summary>
@ -50,7 +26,10 @@ namespace MediaBrowser.Common.Plugins
/// <summary>
/// Gets the type of configuration this plugin uses
/// </summary>
public abstract Type ConfigurationType { get; }
public virtual Type ConfigurationType
{
get { return typeof (BasePluginConfiguration); }
}
/// <summary>
/// Gets the plugin version

View File

@ -0,0 +1,14 @@

namespace MediaBrowser.Common.Plugins
{
public abstract class BaseTheme : BasePlugin
{
public sealed override bool DownloadToUi
{
get
{
return true;
}
}
}
}

View File

@ -5,6 +5,7 @@ using Microsoft.Shell;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
namespace MediaBrowser.Common.UI
@ -42,6 +43,9 @@ namespace MediaBrowser.Common.UI
Kernel = InstantiateKernel();
var progress = new Progress<TaskProgress>();
progress.ProgressChanged += progress_ProgressChanged;
var splash = new Splash(progress);
splash.Show();
@ -52,6 +56,8 @@ namespace MediaBrowser.Common.UI
await Kernel.Init(progress);
progress.ProgressChanged -= progress_ProgressChanged;
Logger.LogInfo("Kernel.Init completed in {0} seconds.", (DateTime.UtcNow - now).TotalSeconds);
splash.Close();
@ -63,6 +69,8 @@ namespace MediaBrowser.Common.UI
}
catch (Exception ex)
{
progress.ProgressChanged -= progress_ProgressChanged;
if (Logger.LoggerInstance != null)
{
Logger.LogException(ex);
@ -76,6 +84,41 @@ namespace MediaBrowser.Common.UI
}
}
public async Task ReloadKernel()
{
var progress = new Progress<TaskProgress>();
progress.ProgressChanged += progress_ProgressChanged;
try
{
DateTime now = DateTime.UtcNow;
await Kernel.Reload(progress);
progress.ProgressChanged -= progress_ProgressChanged;
Logger.LogInfo("Kernel.Reload completed in {0} seconds.", (DateTime.UtcNow - now).TotalSeconds);
}
catch (Exception ex)
{
progress.ProgressChanged -= progress_ProgressChanged;
Logger.LogException(ex);
// Shutdown the app with an error code
Shutdown(1);
}
}
void progress_ProgressChanged(object sender, TaskProgress e)
{
if (Logger.LoggerInstance != null)
{
Logger.LogInfo(e.Description);
}
}
protected virtual void OnKernelLoaded()
{

View File

@ -78,28 +78,53 @@ namespace MediaBrowser.Controller
: base()
{
Instance = this;
}
/// <summary>
/// Performs initializations that only occur once
/// </summary>
protected override void InitializeInternal(IProgress<TaskProgress> progress)
{
ItemController = new ItemController();
DirectoryWatchers = new DirectoryWatchers();
WeatherClient = new WeatherClient();
ItemController.PreBeginResolvePath += ItemController_PreBeginResolvePath;
ItemController.BeginResolvePath += ItemController_BeginResolvePath;
base.InitializeInternal(progress);
}
public async override Task Init(IProgress<TaskProgress> progress)
/// <summary>
/// Performs initializations that can be reloaded at anytime
/// </summary>
public override async Task Reload(IProgress<TaskProgress> progress)
{
ExtractFFMpeg();
await base.Reload(progress).ConfigureAwait(false);
await base.Init(progress).ConfigureAwait(false);
ReloadWeatherClient();
ExtractFFMpeg();
progress.Report(new TaskProgress { Description = "Loading Users", PercentComplete = 15 });
ReloadUsers();
progress.Report(new TaskProgress { Description = "Loading Media Library", PercentComplete = 25 });
await ReloadRoot(allowInternetProviders: false).ConfigureAwait(false);
}
progress.Report(new TaskProgress { Description = "Loading Complete", PercentComplete = 100 });
/// <summary>
/// Completely disposes the Kernel
/// </summary>
public override void Dispose()
{
base.Dispose();
DirectoryWatchers.Stop();
DisposeWeatherClient();
ItemController.PreBeginResolvePath -= ItemController_PreBeginResolvePath;
ItemController.BeginResolvePath -= ItemController_BeginResolvePath;
}
protected override void OnComposablePartsLoaded()
@ -290,6 +315,7 @@ namespace MediaBrowser.Controller
user.Name = "Default User";
user.Id = Guid.Parse("5d1cf7fce25943b790d140095457a42b");
user.PrimaryImagePath = "D:\\Video\\TV\\Archer (2009)\\backdrop.jpg";
list.Add(user);
user = new User { };
@ -387,5 +413,26 @@ namespace MediaBrowser.Controller
}
}
}
/// <summary>
/// Disposes the current WeatherClient
/// </summary>
private void DisposeWeatherClient()
{
if (WeatherClient != null)
{
WeatherClient.Dispose();
}
}
/// <summary>
/// Disposes the current WeatherClient and creates a new one
/// </summary>
private void ReloadWeatherClient()
{
DisposeWeatherClient();
WeatherClient = new WeatherClient();
}
}
}

View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{6E892999-711D-4E24-8BAC-DACF5BFA783A}</ProjectGuid>
<OutputType>library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MediaBrowser.Plugins.DefaultTheme</RootNamespace>
<AssemblyName>MediaBrowser.Plugins.DefaultTheme</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<Compile Include="Plugin.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<AppDesigner Include="Properties\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\MediaBrowserServer\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
<Name>MediaBrowser.Common</Name>
</ProjectReference>
<ProjectReference Include="..\..\MediaBrowserServer\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
<Name>MediaBrowser.Controller</Name>
</ProjectReference>
<ProjectReference Include="..\..\MediaBrowserServer\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
<ProjectReference Include="..\..\MediaBrowserUI\MediaBrowser.UI\MediaBrowser.UI.csproj">
<Project>{b5ece1fb-618e-420b-9a99-8e972d76920a}</Project>
<Name>MediaBrowser.UI</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>xcopy "$(TargetPath)" "$(SolutionDir)\ProgramData\Plugins\" /y</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,19 @@
using MediaBrowser.Common.Plugins;
using System.ComponentModel.Composition;
namespace MediaBrowser.Plugins.DefaultTheme
{
[Export(typeof(BasePlugin))]
public class Plugin : BaseTheme
{
public override string Name
{
get { return "Default Theme"; }
}
protected override void InitializeInUi()
{
base.InitializeInUi();
}
}
}

View File

@ -0,0 +1,55 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("MediaBrowser.Plugins.DefaultTheme")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("MediaBrowser.Plugins.DefaultTheme")]
[assembly: AssemblyCopyright("Copyright © 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly:ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,62 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MediaBrowser.Plugins.DefaultTheme.Properties {
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if ((resourceMan == null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MediaBrowser.Plugins.DefaultTheme.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MediaBrowser.Plugins.DefaultTheme.Properties
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
return defaultInstance;
}
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

50
MediaBrowser.Plugins.sln Normal file
View File

@ -0,0 +1,50 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Plugins.DefaultTheme", "MediaBrowser.Plugins.DefaultTheme\MediaBrowser.Plugins.DefaultTheme.csproj", "{6E892999-711D-4E24-8BAC-DACF5BFA783A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Controller", "MediaBrowser.Controller\MediaBrowser.Controller.csproj", "{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.UI", "..\MediaBrowserUI\MediaBrowser.UI\MediaBrowser.UI.csproj", "{B5ECE1FB-618E-420B-9A99-8E972D76920A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Common", "MediaBrowser.Common\MediaBrowser.Common.csproj", "{9142EEFA-7570-41E1-BFCC-468BB571AF2F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Model", "MediaBrowser.Model\MediaBrowser.Model.csproj", "{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.ApiInteraction", "MediaBrowser.ApiInteraction\MediaBrowser.ApiInteraction.csproj", "{921C0F64-FDA7-4E9F-9E73-0CB0EEDB2422}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6E892999-711D-4E24-8BAC-DACF5BFA783A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6E892999-711D-4E24-8BAC-DACF5BFA783A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6E892999-711D-4E24-8BAC-DACF5BFA783A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6E892999-711D-4E24-8BAC-DACF5BFA783A}.Release|Any CPU.Build.0 = Release|Any CPU
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|Any CPU.Build.0 = Release|Any CPU
{B5ECE1FB-618E-420B-9A99-8E972D76920A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5ECE1FB-618E-420B-9A99-8E972D76920A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5ECE1FB-618E-420B-9A99-8E972D76920A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5ECE1FB-618E-420B-9A99-8E972D76920A}.Release|Any CPU.Build.0 = Release|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|Any CPU.Build.0 = Release|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|Any CPU.Build.0 = Release|Any CPU
{921C0F64-FDA7-4E9F-9E73-0CB0EEDB2422}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{921C0F64-FDA7-4E9F-9E73-0CB0EEDB2422}.Debug|Any CPU.Build.0 = Debug|Any CPU
{921C0F64-FDA7-4E9F-9E73-0CB0EEDB2422}.Release|Any CPU.ActiveCfg = Release|Any CPU
{921C0F64-FDA7-4E9F-9E73-0CB0EEDB2422}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -4,17 +4,38 @@
xmlns:tb="http://www.hardcodet.net/taskbar"
Title="MainWindow" Height="350" Width="525" AllowsTransparency="True" Background="Transparent" WindowStyle="None" ShowInTaskbar="False">
<Grid>
<tb:TaskbarIcon Name="MbTaskbarIcon" IconSource="/Resources/Images/Icon.ico" ToolTipText="MediaBrowser Server">
<tb:TaskbarIcon Name="MbTaskbarIcon" ToolTipText="MediaBrowser Server">
<tb:TaskbarIcon.ContextMenu>
<ContextMenu Background="White">
<MenuItem Name="cmOpenDashboard" Header="Open Dashboard" Click="cmOpenDashboard_click"/>
<MenuItem Name="cmdReloadServer" Header="Reload Server" Click="cmdReloadServer_click"/>
<MenuItem Name="cmVisitCT" Header="Visit Community Tracker" Click="cmVisitCT_click"/>
<Separator/>
<MenuItem Name="cmExit" Header="Exit" Click="cmExit_click"/>
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
<tb:TaskbarIcon.Style>
<Style TargetType="{x:Type tb:TaskbarIcon}">
<Setter Property="IconSource" Value="/Resources/Images/icon.ico" />
<Style.Triggers>
<DataTrigger Binding="{Binding LoadingImageIndex}" Value="1">
<Setter Property="IconSource" Value="/Resources/Images/loadingIcon1.ico" />
</DataTrigger>
<DataTrigger Binding="{Binding LoadingImageIndex}" Value="2">
<Setter Property="IconSource" Value="/Resources/Images/loadingIcon2.ico" />
</DataTrigger>
<DataTrigger Binding="{Binding LoadingImageIndex}" Value="3">
<Setter Property="IconSource" Value="/Resources/Images/loadingIcon3.ico" />
</DataTrigger>
<DataTrigger Binding="{Binding LoadingImageIndex}" Value="4">
<Setter Property="IconSource" Value="/Resources/Images/loadingIcon4.ico" />
</DataTrigger>
</Style.Triggers>
</Style>
</tb:TaskbarIcon.Style>
</tb:TaskbarIcon>
</Grid>
</Window>

View File

@ -1,4 +1,7 @@
using System.Diagnostics;
using Hardcodet.Wpf.TaskbarNotification;
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows;
namespace MediaBrowser.ServerApplication
@ -6,12 +9,38 @@ namespace MediaBrowser.ServerApplication
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
//LoadKernel();
Loaded += MainWindow_Loaded;
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private int _loadingImageIndex;
public int LoadingImageIndex
{
get { return _loadingImageIndex; }
set
{
_loadingImageIndex = value;
OnPropertyChanged("LoadingImageIndex");
}
}
#region Context Menu events
@ -31,6 +60,36 @@ namespace MediaBrowser.ServerApplication
Close();
}
private async void cmdReloadServer_click(object sender, RoutedEventArgs e)
{
MbTaskbarIcon.ShowBalloonTip("Media Browser is reloading", "Please wait...", BalloonIcon.Info);
LoadingImageIndex = 0;
Timer timer = new Timer(LoadingIconTimerCallback, null, 0, 250);
await (Application.Current as App).ReloadKernel().ConfigureAwait(false);
timer.Dispose();
LoadingImageIndex = 0;
}
private void LoadingIconTimerCallback(object stateInfo)
{
const int numImages = 4;
if (LoadingImageIndex < numImages)
{
LoadingImageIndex++;
}
else
{
LoadingImageIndex = 1;
}
}
#endregion
}
}

View File

@ -121,15 +121,16 @@
<Name>MediaBrowser.Model</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\Images\icon16.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\Images\icon.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\Images\loadingIcon1-16.ico" />
<Resource Include="Resources\Images\loadingIcon2-16.ico" />
<Resource Include="Resources\Images\loadingIcon1.ico" />
<Resource Include="Resources\Images\loadingIcon2.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\Images\loadingIcon3.ico" />
<Resource Include="Resources\Images\loadingIcon4.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

View File

@ -5,7 +5,7 @@ using System.ComponentModel.Composition;
namespace MediaBrowser.WebDashboard
{
[Export(typeof(BasePlugin))]
public class Plugin : BaseGenericPlugin<BasePluginConfiguration>
public class Plugin : BasePlugin
{
public override string Name
{