aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Plugins.MpcHc
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Plugins.MpcHc')
-rw-r--r--MediaBrowser.Plugins.MpcHc/MediaBrowser.Plugins.MpcHc.csproj109
-rw-r--r--MediaBrowser.Plugins.MpcHc/MpcHcMediaPlayer.cs574
-rw-r--r--MediaBrowser.Plugins.MpcHc/Plugin.cs32
-rw-r--r--MediaBrowser.Plugins.MpcHc/Properties/AssemblyInfo.cs53
-rw-r--r--MediaBrowser.Plugins.MpcHc/Properties/Resources.Designer.cs62
-rw-r--r--MediaBrowser.Plugins.MpcHc/Properties/Resources.resx117
-rw-r--r--MediaBrowser.Plugins.MpcHc/Properties/Settings.Designer.cs30
-rw-r--r--MediaBrowser.Plugins.MpcHc/Properties/Settings.settings7
-rw-r--r--MediaBrowser.Plugins.MpcHc/app.config11
9 files changed, 995 insertions, 0 deletions
diff --git a/MediaBrowser.Plugins.MpcHc/MediaBrowser.Plugins.MpcHc.csproj b/MediaBrowser.Plugins.MpcHc/MediaBrowser.Plugins.MpcHc.csproj
new file mode 100644
index 000000000..f95b73b65
--- /dev/null
+++ b/MediaBrowser.Plugins.MpcHc/MediaBrowser.Plugins.MpcHc.csproj
@@ -0,0 +1,109 @@
+<?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>{2E94BC08-A7A2-42DD-9893-EAA3DACD1CF9}</ProjectGuid>
+ <OutputType>library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>MediaBrowser.Plugins.MpcHc</RootNamespace>
+ <AssemblyName>MediaBrowser.Plugins.MpcHc</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <WarningLevel>4</WarningLevel>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
+ <RestorePackages>true</RestorePackages>
+ </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>
+ <PropertyGroup>
+ <RunPostBuildEvent>Always</RunPostBuildEvent>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.ComponentModel.Composition" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Net.Http" />
+ <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="MpcHcMediaPlayer.cs" />
+ <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="app.config" />
+ <None Include="Properties\Settings.settings">
+ <Generator>SettingsSingleFileGenerator</Generator>
+ <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+ </None>
+ <AppDesigner Include="Properties\" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
+ <Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
+ <Name>MediaBrowser.Common</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
+ <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
+ <Name>MediaBrowser.Model</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\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-Server\Plugins\" /y</PostBuildEvent>
+ </PropertyGroup>
+ <Import Project="$(SolutionDir)\.nuget\nuget.targets" />
+ <!-- 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> \ No newline at end of file
diff --git a/MediaBrowser.Plugins.MpcHc/MpcHcMediaPlayer.cs b/MediaBrowser.Plugins.MpcHc/MpcHcMediaPlayer.cs
new file mode 100644
index 000000000..bbeff2206
--- /dev/null
+++ b/MediaBrowser.Plugins.MpcHc/MpcHcMediaPlayer.cs
@@ -0,0 +1,574 @@
+using MediaBrowser.Common.Logging;
+using MediaBrowser.Model.DTO;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.UI.Configuration;
+using MediaBrowser.UI.Controller;
+using MediaBrowser.UI.Playback;
+using MediaBrowser.UI.Playback.ExternalPlayer;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.IO;
+using System.Linq;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Plugins.MpcHc
+{
+ /// <summary>
+ /// Class GenericExternalPlayer
+ /// </summary>
+ [Export(typeof(BaseMediaPlayer))]
+ public class MpcHcMediaPlayer : BaseExternalPlayer
+ {
+ /// <summary>
+ /// The state sync lock
+ /// </summary>
+ private object stateSyncLock = new object();
+
+ /// <summary>
+ /// The MPC HTTP interface resource pool
+ /// </summary>
+ private SemaphoreSlim MpcHttpInterfaceResourcePool = new SemaphoreSlim(1, 1);
+
+ /// <summary>
+ /// Gets or sets the HTTP interface cancellation token.
+ /// </summary>
+ /// <value>The HTTP interface cancellation token.</value>
+ private CancellationTokenSource HttpInterfaceCancellationTokenSource { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance has started playing.
+ /// </summary>
+ /// <value><c>true</c> if this instance has started playing; otherwise, <c>false</c>.</value>
+ private bool HasStartedPlaying { get; set; }
+
+ /// <summary>
+ /// Gets or sets the status update timer.
+ /// </summary>
+ /// <value>The status update timer.</value>
+ private Timer StatusUpdateTimer { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance can monitor progress.
+ /// </summary>
+ /// <value><c>true</c> if this instance can monitor progress; otherwise, <c>false</c>.</value>
+ protected override bool CanMonitorProgress
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// The _current position ticks
+ /// </summary>
+ private long? _currentPositionTicks;
+
+ /// <summary>
+ /// Gets the current position ticks.
+ /// </summary>
+ /// <value>The current position ticks.</value>
+ public override long? CurrentPositionTicks
+ {
+ get
+ {
+ return _currentPositionTicks;
+ }
+ }
+
+ /// <summary>
+ /// The _current playlist index
+ /// </summary>
+ private int _currentPlaylistIndex;
+
+ /// <summary>
+ /// Gets the index of the current playlist.
+ /// </summary>
+ /// <value>The index of the current playlist.</value>
+ public override int CurrentPlaylistIndex
+ {
+ get
+ {
+ return _currentPlaylistIndex;
+ }
+ }
+
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public override string Name
+ {
+ get { return "MpcHc"; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance can close automatically.
+ /// </summary>
+ /// <value><c>true</c> if this instance can close automatically; otherwise, <c>false</c>.</value>
+ protected override bool CanCloseAutomatically
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Determines whether this instance can play the specified item.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <returns><c>true</c> if this instance can play the specified item; otherwise, <c>false</c>.</returns>
+ public override bool CanPlay(DtoBaseItem item)
+ {
+ return item.IsVideo || item.IsAudio;
+ }
+
+ /// <summary>
+ /// Gets the command arguments.
+ /// </summary>
+ /// <param name="items">The items.</param>
+ /// <param name="options">The options.</param>
+ /// <param name="playerConfiguration">The player configuration.</param>
+ /// <returns>System.String.</returns>
+ protected override string GetCommandArguments(List<DtoBaseItem> items, PlayOptions options, PlayerConfiguration playerConfiguration)
+ {
+ var formatString = "{0} /play /fullscreen /close";
+
+ var firstItem = items[0];
+
+ var startTicks = Math.Max(options.StartPositionTicks, 0);
+
+ if (startTicks > 0 && firstItem.IsVideo && firstItem.VideoType.HasValue && firstItem.VideoType.Value == VideoType.Dvd)
+ {
+ formatString += " /dvdpos 1#" + TimeSpan.FromTicks(startTicks).ToString("hh\\:mm\\:ss");
+ }
+ else
+ {
+ formatString += " /start " + TimeSpan.FromTicks(startTicks).TotalMilliseconds;
+ }
+
+
+ return GetCommandArguments(items, formatString);
+ }
+
+ /// <summary>
+ /// Gets the path for command line.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <returns>System.String.</returns>
+ protected override string GetPathForCommandLine(DtoBaseItem item)
+ {
+ var path = base.GetPathForCommandLine(item);
+
+ if (item.IsVideo && item.VideoType.HasValue)
+ {
+ if (item.VideoType.Value == VideoType.Dvd)
+ {
+ // Point directly to the video_ts path
+ // Otherwise mpc will play any other media files that might exist in the dvd top folder (e.g. video backdrops).
+ var videoTsPath = Path.Combine(path, "video_ts");
+
+ if (Directory.Exists(videoTsPath))
+ {
+ path = videoTsPath;
+ }
+ }
+ if (item.VideoType.Value == VideoType.BluRay)
+ {
+ // Point directly to the bdmv path
+ var bdmvPath = Path.Combine(path, "bdmv");
+
+ if (Directory.Exists(bdmvPath))
+ {
+ path = bdmvPath;
+ }
+ }
+ }
+
+ return FormatPath(path);
+ }
+
+ /// <summary>
+ /// Formats the path.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <returns>System.String.</returns>
+ private string FormatPath(string path)
+ {
+ if (path.EndsWith(":\\", StringComparison.OrdinalIgnoreCase))
+ {
+ path = path.TrimEnd('\\');
+ }
+
+ return path;
+ }
+
+ /// <summary>
+ /// Called when [external player launched].
+ /// </summary>
+ protected override void OnExternalPlayerLaunched()
+ {
+ base.OnExternalPlayerLaunched();
+
+ ReloadStatusUpdateTimer();
+ }
+
+ /// <summary>
+ /// Reloads the status update timer.
+ /// </summary>
+ private void ReloadStatusUpdateTimer()
+ {
+ DisposeStatusTimer();
+
+ HttpInterfaceCancellationTokenSource = new CancellationTokenSource();
+
+ StatusUpdateTimer = new Timer(OnStatusUpdateTimerStopped, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
+ }
+
+ /// <summary>
+ /// Called when [status update timer stopped].
+ /// </summary>
+ /// <param name="state">The state.</param>
+ private async void OnStatusUpdateTimerStopped(object state)
+ {
+ try
+ {
+ var token = HttpInterfaceCancellationTokenSource.Token;
+
+ using (var stream = await UIKernel.Instance.HttpManager.Get(StatusUrl, MpcHttpInterfaceResourcePool, token).ConfigureAwait(false))
+ {
+ token.ThrowIfCancellationRequested();
+
+ using (var reader = new StreamReader(stream))
+ {
+ token.ThrowIfCancellationRequested();
+
+ var result = await reader.ReadToEndAsync().ConfigureAwait(false);
+
+ token.ThrowIfCancellationRequested();
+
+ ProcessStatusResult(result);
+ }
+ }
+ }
+ catch (HttpRequestException ex)
+ {
+ Logger.ErrorException("Error connecting to MpcHc status interface", ex);
+ }
+ catch (OperationCanceledException)
+ {
+ // Manually cancelled by us
+ Logger.Info("Status request cancelled");
+ }
+ }
+
+ /// <summary>
+ /// Processes the status result.
+ /// </summary>
+ /// <param name="result">The result.</param>
+ private async void ProcessStatusResult(string result)
+ {
+ // Sample result
+ // OnStatus('test.avi', 'Playing', 5292, '00:00:05', 1203090, '00:20:03', 0, 100, 'C:\test.avi')
+ // 5292 = position in ms
+ // 00:00:05 = position
+ // 1203090 = duration in ms
+ // 00:20:03 = duration
+
+ var quoteChar = result.IndexOf(", \"", StringComparison.OrdinalIgnoreCase) == -1 ? '\'' : '\"';
+
+ // Strip off the leading "OnStatus(" and the trailing ")"
+ result = result.Substring(result.IndexOf(quoteChar));
+ result = result.Substring(0, result.LastIndexOf(quoteChar));
+
+ // Strip off the filename at the beginning
+ result = result.Substring(result.IndexOf(string.Format("{0}, {0}", quoteChar), StringComparison.OrdinalIgnoreCase) + 3);
+
+ // Find the last index of ", '" so that we can extract and then strip off the file path at the end.
+ var lastIndexOfSeparator = result.LastIndexOf(", " + quoteChar, StringComparison.OrdinalIgnoreCase);
+
+ // Get the current playing file path
+ var currentPlayingFile = result.Substring(lastIndexOfSeparator + 2).Trim(quoteChar);
+
+ // Strip off the current playing file path
+ result = result.Substring(0, lastIndexOfSeparator);
+
+ var values = result.Split(',').Select(v => v.Trim().Trim(quoteChar)).ToList();
+
+ var currentPositionTicks = TimeSpan.FromMilliseconds(double.Parse(values[1])).Ticks;
+ //var currentDurationTicks = TimeSpan.FromMilliseconds(double.Parse(values[3])).Ticks;
+
+ var playstate = values[0];
+
+ var playlistIndex = GetPlaylistIndex(currentPlayingFile);
+
+ if (playstate.Equals("stopped", StringComparison.OrdinalIgnoreCase))
+ {
+ if (HasStartedPlaying)
+ {
+ await ClosePlayer().ConfigureAwait(false);
+ }
+ }
+ else
+ {
+ lock (stateSyncLock)
+ {
+ if (_currentPlaylistIndex != playlistIndex)
+ {
+ OnMediaChanged(_currentPlaylistIndex, _currentPositionTicks, playlistIndex);
+ }
+
+ _currentPositionTicks = currentPositionTicks;
+ _currentPlaylistIndex = playlistIndex;
+ }
+
+ if (playstate.Equals("playing", StringComparison.OrdinalIgnoreCase))
+ {
+ HasStartedPlaying = true;
+ PlayState = PlayState.Playing;
+ }
+ else if (playstate.Equals("paused", StringComparison.OrdinalIgnoreCase))
+ {
+ HasStartedPlaying = true;
+ PlayState = PlayState.Paused;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the index of the playlist.
+ /// </summary>
+ /// <param name="nowPlayingPath">The now playing path.</param>
+ /// <returns>System.Int32.</returns>
+ private int GetPlaylistIndex(string nowPlayingPath)
+ {
+ for (var i = 0; i < Playlist.Count; i++)
+ {
+ var item = Playlist[i];
+
+ var pathArg = GetPathForCommandLine(item);
+
+ if (pathArg.Equals(nowPlayingPath, StringComparison.OrdinalIgnoreCase))
+ {
+ return i;
+ }
+
+ if (item.VideoType.HasValue)
+ {
+ if (item.VideoType.Value == VideoType.BluRay || item.VideoType.Value == VideoType.Dvd || item.VideoType.Value == VideoType.HdDvd)
+ {
+ if (nowPlayingPath.StartsWith(pathArg, StringComparison.OrdinalIgnoreCase))
+ {
+ return i;
+ }
+ }
+ }
+ }
+ return -1;
+ }
+
+ /// <summary>
+ /// Called when [player stopped internal].
+ /// </summary>
+ protected override void OnPlayerStoppedInternal()
+ {
+ HttpInterfaceCancellationTokenSource.Cancel();
+
+ DisposeStatusTimer();
+ _currentPositionTicks = null;
+ _currentPlaylistIndex = 0;
+ HasStartedPlaying = false;
+ HttpInterfaceCancellationTokenSource = null;
+
+ base.OnPlayerStoppedInternal();
+ }
+
+ /// <summary>
+ /// Disposes the status timer.
+ /// </summary>
+ private void DisposeStatusTimer()
+ {
+ if (StatusUpdateTimer != null)
+ {
+ StatusUpdateTimer.Dispose();
+ }
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources.
+ /// </summary>
+ /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected override void Dispose(bool dispose)
+ {
+ if (dispose)
+ {
+ DisposeStatusTimer();
+
+ MpcHttpInterfaceResourcePool.Dispose();
+ }
+
+ base.Dispose(dispose);
+ }
+
+ /// <summary>
+ /// Seeks the internal.
+ /// </summary>
+ /// <param name="positionTicks">The position ticks.</param>
+ /// <returns>Task.</returns>
+ protected override Task SeekInternal(long positionTicks)
+ {
+ var additionalParams = new Dictionary<string, string>();
+
+ var time = TimeSpan.FromTicks(positionTicks);
+
+ var timeString = time.Hours + ":" + time.Minutes + ":" + time.Seconds;
+
+ additionalParams.Add("position", timeString);
+
+ return SendCommandToPlayer("-1", additionalParams);
+ }
+
+ /// <summary>
+ /// Pauses the internal.
+ /// </summary>
+ /// <returns>Task.</returns>
+ protected override Task PauseInternal()
+ {
+ return SendCommandToPlayer("888", new Dictionary<string, string>());
+ }
+
+ /// <summary>
+ /// Uns the pause internal.
+ /// </summary>
+ /// <returns>Task.</returns>
+ protected override Task UnPauseInternal()
+ {
+ return SendCommandToPlayer("887", new Dictionary<string, string>());
+ }
+
+ /// <summary>
+ /// Stops the internal.
+ /// </summary>
+ /// <returns>Task.</returns>
+ protected override Task StopInternal()
+ {
+ return SendCommandToPlayer("890", new Dictionary<string, string>());
+ }
+
+ /// <summary>
+ /// Closes the player.
+ /// </summary>
+ /// <returns>Task.</returns>
+ protected Task ClosePlayer()
+ {
+ return SendCommandToPlayer("816", new Dictionary<string, string>());
+ }
+
+ /// <summary>
+ /// Sends a command to MPC using the HTTP interface
+ /// http://www.autohotkey.net/~specter333/MPC/HTTP%20Commands.txt
+ /// </summary>
+ /// <param name="commandNumber">The command number.</param>
+ /// <param name="additionalParams">The additional params.</param>
+ /// <returns>Task.</returns>
+ /// <exception cref="System.ArgumentNullException">commandNumber</exception>
+ private async Task SendCommandToPlayer(string commandNumber, Dictionary<string, string> additionalParams)
+ {
+ if (string.IsNullOrEmpty(commandNumber))
+ {
+ throw new ArgumentNullException("commandNumber");
+ }
+
+ if (additionalParams == null)
+ {
+ throw new ArgumentNullException("additionalParams");
+ }
+
+ var url = CommandUrl + "?wm_command=" + commandNumber;
+
+ url = additionalParams.Keys.Aggregate(url, (current, name) => current + ("&" + name + "=" + additionalParams[name]));
+
+ Logger.Info("Sending command to MPC: " + url);
+
+ try
+ {
+ using (var stream = await UIKernel.Instance.HttpManager.Get(url, MpcHttpInterfaceResourcePool, HttpInterfaceCancellationTokenSource.Token).ConfigureAwait(false))
+ {
+ }
+ }
+ catch (HttpRequestException ex)
+ {
+ Logger.ErrorException("Error connecting to MpcHc command interface", ex);
+ }
+ catch (OperationCanceledException)
+ {
+ // Manually cancelled by us
+ Logger.Info("Command request cancelled");
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance can pause.
+ /// </summary>
+ /// <value><c>true</c> if this instance can pause; otherwise, <c>false</c>.</value>
+ public override bool CanPause
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Gets the server name that the http interface will be running on
+ /// </summary>
+ /// <value>The HTTP server.</value>
+ private string HttpServer
+ {
+ get
+ {
+ return "localhost";
+ }
+ }
+
+ /// <summary>
+ /// Gets the port that the web interface will be running on
+ /// </summary>
+ /// <value>The HTTP port.</value>
+ private string HttpPort
+ {
+ get
+ {
+ return "13579";
+ }
+ }
+
+ /// <summary>
+ /// Gets the url of that will be called to for status
+ /// </summary>
+ /// <value>The status URL.</value>
+ private string StatusUrl
+ {
+ get
+ {
+ return "http://" + HttpServer + ":" + HttpPort + "/status.html";
+ }
+ }
+
+ /// <summary>
+ /// Gets the url of that will be called to send commands
+ /// </summary>
+ /// <value>The command URL.</value>
+ private string CommandUrl
+ {
+ get
+ {
+ return "http://" + HttpServer + ":" + HttpPort + "/command.html";
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Plugins.MpcHc/Plugin.cs b/MediaBrowser.Plugins.MpcHc/Plugin.cs
new file mode 100644
index 000000000..ca8acc94e
--- /dev/null
+++ b/MediaBrowser.Plugins.MpcHc/Plugin.cs
@@ -0,0 +1,32 @@
+using MediaBrowser.Common.Plugins;
+using MediaBrowser.Model.Plugins;
+using System;
+using System.ComponentModel.Composition;
+
+namespace MediaBrowser.Plugins.MpcHc
+{
+ /// <summary>
+ /// Class Plugin
+ /// </summary>
+ [Export(typeof(IPlugin))]
+ public class Plugin : BaseUiPlugin<BasePluginConfiguration>
+ {
+ /// <summary>
+ /// Gets the name of the plugin
+ /// </summary>
+ /// <value>The name.</value>
+ public override string Name
+ {
+ get { return "MPC-HC Integration"; }
+ }
+
+ /// <summary>
+ /// Gets the minimum required UI version.
+ /// </summary>
+ /// <value>The minimum required UI version.</value>
+ public override Version MinimumRequiredUIVersion
+ {
+ get { return new Version("2.9.4782.23738"); }
+ }
+ }
+}
diff --git a/MediaBrowser.Plugins.MpcHc/Properties/AssemblyInfo.cs b/MediaBrowser.Plugins.MpcHc/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..10442b136
--- /dev/null
+++ b/MediaBrowser.Plugins.MpcHc/Properties/AssemblyInfo.cs
@@ -0,0 +1,53 @@
+using System.Reflection;
+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.MpcHc")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("MediaBrowser.Plugins.MpcHc")]
+[assembly: AssemblyCopyright("Copyright © 2013")]
+[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)
+)]
+
+[assembly: Guid("F6D17656-25FE-4564-9246-B4584F797348")]
+
+// 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.*")]
diff --git a/MediaBrowser.Plugins.MpcHc/Properties/Resources.Designer.cs b/MediaBrowser.Plugins.MpcHc/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..14f2a3ba2
--- /dev/null
+++ b/MediaBrowser.Plugins.MpcHc/Properties/Resources.Designer.cs
@@ -0,0 +1,62 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.18010
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace MediaBrowser.Plugins.MpcHc.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.MpcHc.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;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Plugins.MpcHc/Properties/Resources.resx b/MediaBrowser.Plugins.MpcHc/Properties/Resources.resx
new file mode 100644
index 000000000..af7dbebba
--- /dev/null
+++ b/MediaBrowser.Plugins.MpcHc/Properties/Resources.resx
@@ -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> \ No newline at end of file
diff --git a/MediaBrowser.Plugins.MpcHc/Properties/Settings.Designer.cs b/MediaBrowser.Plugins.MpcHc/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..f64175f33
--- /dev/null
+++ b/MediaBrowser.Plugins.MpcHc/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.18010
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace MediaBrowser.Plugins.MpcHc.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;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Plugins.MpcHc/Properties/Settings.settings b/MediaBrowser.Plugins.MpcHc/Properties/Settings.settings
new file mode 100644
index 000000000..033d7a5e9
--- /dev/null
+++ b/MediaBrowser.Plugins.MpcHc/Properties/Settings.settings
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
+ <Profiles>
+ <Profile Name="(Default)" />
+ </Profiles>
+ <Settings />
+</SettingsFile> \ No newline at end of file
diff --git a/MediaBrowser.Plugins.MpcHc/app.config b/MediaBrowser.Plugins.MpcHc/app.config
new file mode 100644
index 000000000..29abde1f6
--- /dev/null
+++ b/MediaBrowser.Plugins.MpcHc/app.config
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <runtime>
+ <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+ <dependentAssembly>
+ <assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" />
+ <bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
+ </dependentAssembly>
+ </assemblyBinding>
+ </runtime>
+</configuration> \ No newline at end of file