rainmeter-studio/Plugins/PluginNowPlaying/PlayerWMP.cpp

645 lines
11 KiB
C++

/*
Copyright (C) 2011 Birunthan Mohanathas (www.poiru.net)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "StdAfx.h"
#include "PlayerWMP.h"
CPlayer* CPlayerWMP::c_Player = NULL;
extern HINSTANCE g_Instance;
/*
** CRemoteHost
**
** Constructor.
**
*/
CPlayerWMP::CRemoteHost::CRemoteHost() :
m_Player()
{
}
/*
** ~CRemoteHost
**
** Destructor.
**
*/
CPlayerWMP::CRemoteHost::~CRemoteHost()
{
}
HRESULT CPlayerWMP::CRemoteHost::QueryService(REFGUID guidService, REFIID riid, void** ppv)
{
return ppv ? QueryInterface(riid, ppv) : E_POINTER;
}
HRESULT CPlayerWMP::CRemoteHost::GetServiceType(BSTR* pbstrType)
{
HRESULT hr = E_POINTER;
if (pbstrType)
{
*pbstrType = SysAllocString(L"RemoteNoDialogs");
hr = *pbstrType? S_OK : E_POINTER;
}
return hr;
}
HRESULT CPlayerWMP::CRemoteHost::GetApplicationName(BSTR* pbstrName)
{
HRESULT hr = E_POINTER;
if (pbstrName)
{
CComBSTR bstrAppName = L"Rainmeter NowPlaying";
*pbstrName = bstrAppName.Detach();
hr = *pbstrName? S_OK : E_POINTER;
}
return hr;
}
HRESULT CPlayerWMP::CRemoteHost::GetScriptableObject(BSTR* pbstrName, IDispatch** ppDispatch)
{
if (pbstrName)
{
*pbstrName = NULL;
}
if (ppDispatch)
{
*ppDispatch = NULL;
}
return E_NOTIMPL;
}
HRESULT CPlayerWMP::CRemoteHost::GetCustomUIMode(BSTR* pbstrFile)
{
return E_POINTER;
}
/*
** CurrentItemChange
**
** Called when playing track changes.
**
*/
void CPlayerWMP::CRemoteHost::CurrentItemChange(IDispatch* pdispMedia)
{
m_Player->m_TrackChanged = true;
}
/*
** PlayStateChange
**
** Called when play state changes.
**
*/
void CPlayerWMP::CRemoteHost::PlayStateChange(long NewState)
{
switch (NewState)
{
case wmppsStopped:
case wmppsMediaEnded:
m_Player->ClearData();
break;
case wmppsPaused:
m_Player->m_State = STATE_PAUSED;
break;
case wmppsPlaying:
if (m_Player->m_State == STATE_STOPPED)
{
m_Player->m_TrackChanged = true;
}
m_Player->m_State = STATE_PLAYING;
break;
default:
break;
}
}
/*
** SwitchedToControl
**
** Called when WMP quits.
**
*/
void CPlayerWMP::CRemoteHost::SwitchedToControl()
{
m_Player->ClearData();
m_Player->Uninitialize();
}
/*
** CPlayerWMP
**
** Constructor.
**
*/
CPlayerWMP::CPlayerWMP() : CPlayer(),
m_TrackChanged(false),
m_Window(),
m_LastCheckTime(0),
m_ComModule(),
m_AxWindow(),
m_IPlayer(),
m_IControls(),
m_ISettings(),
m_IConnectionPoint()
{
m_ComModule.Init(NULL, NULL, &LIBID_ATLLib);
}
/*
** ~CPlayerWMP
**
** Destructor.
**
*/
CPlayerWMP::~CPlayerWMP()
{
c_Player = NULL;
Uninitialize();
m_ComModule.Term();
}
/*
** Create
**
** Creates a shared class object.
**
*/
CPlayer* CPlayerWMP::Create()
{
if (!c_Player)
{
c_Player = new CPlayerWMP();
}
return c_Player;
}
/*
** Initialize
**
** Set up the COM interface with WMP.
**
*/
void CPlayerWMP::Initialize()
{
// Create windows class
WNDCLASS wc = {0};
wc.hInstance = g_Instance;
wc.lpfnWndProc = DefWindowProc;
wc.lpszClassName = L"NowPlayingWMPClass";
RegisterClass(&wc);
// Create the host window
m_Window = CreateWindow(L"NowPlayingWMPClass",
L"HostWindow",
WS_DISABLED,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
g_Instance,
NULL);
if (!m_Window) return;
CComPtr<IObjectWithSite> spHostObject;
CComPtr<IAxWinHostWindow> spHost;
CComObject<CRemoteHost>* pRemoteHost;
m_AxWindow = new CAxWindow();
HRESULT hr = m_AxWindow ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
m_AxWindow->Create(m_Window, NULL, NULL, WS_CHILD | WS_DISABLED);
if(IsWindow(m_AxWindow->m_hWnd))
{
hr = m_AxWindow->QueryHost(IID_IObjectWithSite, (void**)&spHostObject);
if(!spHostObject.p)
{
hr = E_POINTER;
}
}
}
else
{
return;
}
// Create remote host which implements IServiceProvider and IWMPRemoteMediaServices
if (SUCCEEDED(hr))
{
hr = CComObject<CRemoteHost>::CreateInstance(&pRemoteHost);
if (pRemoteHost)
{
pRemoteHost->AddRef();
pRemoteHost->m_Player = this;
}
else
{
hr = E_POINTER;
}
}
// Set site to the remote host
if (SUCCEEDED(hr))
{
hr = spHostObject->SetSite((IWMPRemoteMediaServices*)pRemoteHost);
}
if (SUCCEEDED(hr))
{
hr = m_AxWindow->QueryHost(&spHost);
if (!spHost)
{
hr = E_NOINTERFACE;
}
}
// Create WMP control
if (SUCCEEDED(hr))
{
hr = spHost->CreateControl(CComBSTR(L"{6BF52A52-394A-11D3-B153-00C04F79FAA6}"), m_AxWindow->m_hWnd, NULL);
}
if (SUCCEEDED(hr))
{
hr = m_AxWindow->QueryControl(&m_IPlayer);
if (!m_IPlayer.p)
{
hr = E_NOINTERFACE;
}
}
// Connect the event interface
CComPtr<IConnectionPointContainer> spConnectionContainer;
hr = m_IPlayer->QueryInterface(&spConnectionContainer);
if (SUCCEEDED(hr))
{
hr = spConnectionContainer->FindConnectionPoint( __uuidof(IWMPEvents), &m_IConnectionPoint);
}
if (SUCCEEDED(hr))
{
DWORD adviseCookie;
hr = m_IConnectionPoint->Advise(pRemoteHost->GetUnknown(), &adviseCookie);
if ((FAILED(hr)) || !adviseCookie)
{
m_IConnectionPoint = NULL;
}
}
// Release remote host object
if (pRemoteHost)
{
pRemoteHost->Release();
}
hr = m_IPlayer->get_controls(&m_IControls);
if (FAILED(hr)) return;
hr = m_IPlayer->get_settings(&m_ISettings);
if (FAILED(hr)) return;
// Get player state
WMPPlayState state;
m_IPlayer->get_playState(&state);
if (state == wmppsPlaying)
{
m_State = STATE_PLAYING;
}
else if (state == wmppsPaused)
{
m_State = STATE_PAUSED;
}
if (m_State != STATE_STOPPED)
{
m_TrackChanged = true;
}
m_Initialized = true;
}
/*
** Uninitialize
**
** Close the interface with WMP.
**
*/
void CPlayerWMP::Uninitialize()
{
if (m_Initialized)
{
m_Initialized = false;
m_IControls.Release();
m_ISettings.Release();
m_IPlayer.Release();
m_AxWindow->DestroyWindow();
delete m_AxWindow;
DestroyWindow(m_Window);
UnregisterClass(L"NowPlayingWMPClass", g_Instance);
}
}
/*
** UpdateData
**
** Called during each update of the main measure.
**
*/
void CPlayerWMP::UpdateData()
{
if (m_Initialized)
{
// Get the volume
long volume;
m_ISettings->get_volume(&volume);
m_Volume = (UINT)volume;
if (m_State != STATE_STOPPED)
{
double position;
m_IControls->get_currentPosition(&position);
m_Position = (UINT)position;
}
if (m_TrackChanged)
{
++m_TrackCount;
m_TrackChanged = false;
CComPtr<IWMPMedia> spMedia;
m_IPlayer->get_currentMedia(&spMedia);
if (spMedia)
{
CComBSTR val;
spMedia->getItemInfo(CComBSTR("Artist"), &val);
m_Artist = val;
spMedia->getItemInfo(CComBSTR("Title"), &val);
m_Title = val;
spMedia->getItemInfo(CComBSTR("Album"), &val);
m_Album = val;
spMedia->getItemInfo(CComBSTR("UserRating"), &val);
int rating = _wtoi(val);
if (rating > 75)
{
m_Rating = 5;
}
else if (rating > 50)
{
m_Rating = 4;
}
else if (rating > 25)
{
m_Rating = 3;
}
else if (rating > 1)
{
m_Rating = 2;
}
else
{
m_Rating = rating;
}
double duration;
spMedia->get_duration(&duration);
m_Duration = (UINT)duration;
CComBSTR url;
spMedia->get_sourceURL(&url);
std::wstring targetPath = url;
if (targetPath != m_FilePath)
{
m_FilePath = targetPath;
// Find cover if needed
// TODO: Fix temp solution
if (m_Measures & MEASURE_COVER || m_InstanceCount == 0)
{
spMedia->getItemInfo(CComBSTR("WM/WMCollectionID"), &val);
targetPath.resize(targetPath.find_last_of(L'\\') + 1);
targetPath += L"AlbumArt_";
targetPath += val;
targetPath += L"_Large.jpg";
if (_waccess(targetPath.c_str(), 0) == 0)
{
m_CoverPath = targetPath;
}
else
{
FindCover();
}
}
if (m_Measures & MEASURE_LYRICS)
{
FindLyrics();
}
}
}
}
}
else
{
DWORD time = GetTickCount();
// Try to find WMP window every 5 seconds
if (m_LastCheckTime = 0 || time - m_LastCheckTime > 5000)
{
m_LastCheckTime = time;
if (FindWindow(L"WMPlayerApp", NULL))
{
Initialize();
}
}
}
}
/*
** Pause
**
** Handles the Pause bang.
**
*/
void CPlayerWMP::Pause()
{
m_IControls->pause();
}
/*
** Play
**
** Handles the Play bang.
**
*/
void CPlayerWMP::Play()
{
m_IControls->play();
}
/*
** Stop
**
** Handles the Stop bang.
**
*/
void CPlayerWMP::Stop()
{
m_IControls->stop();
// TODO: FIXME
m_State = STATE_STOPPED;
}
/*
** Next
**
** Handles the Next bang.
**
*/
void CPlayerWMP::Next()
{
m_IControls->next();
}
/*
** Previous
**
** Handles the Previous bang.
**
*/
void CPlayerWMP::Previous()
{
m_IControls->previous();
}
/*
** SetPosition
**
** Handles the SetPosition bang.
**
*/
void CPlayerWMP::SetPosition(int position)
{
m_IControls->put_currentPosition((double)position);
}
/*
** SetRating
**
** Handles the SetRating bang.
**
*/
void CPlayerWMP::SetRating(int rating)
{
if (m_State != STATE_STOPPED)
{
CComPtr<IWMPMedia> spMedia;
m_IPlayer->get_currentMedia(&spMedia);
if (spMedia)
{
CComBSTR val;
switch (rating)
{
case 0:
val = L"0";
break;
case 1:
val = L"1";
break;
case 2:
val = L"25";
break;
case 3:
val = L"50";
break;
case 4:
val = L"75";
break;
case 5:
val = L"99";
break;
}
spMedia->setItemInfo(CComBSTR("UserRating"), val);
m_Rating = rating;
}
}
}
/*
** SetVolume
**
** Handles the SetVolume bang.
**
*/
void CPlayerWMP::SetVolume(int volume)
{
m_ISettings->put_volume(volume);
}
/*
** ClosePlayer
**
** Handles the ClosePlayer bang.
**
*/
void CPlayerWMP::ClosePlayer()
{
HWND wnd = FindWindow(L"WMPlayerApp", NULL);
if (wnd)
{
SendMessage(wnd, WM_CLOSE, 0, 0);
}
}
/*
** OpenPlayer
**
** Handles the OpenPlayer bang.
**
*/
void CPlayerWMP::OpenPlayer(std::wstring& path)
{
ShellExecute(NULL, L"open", path.empty() ? L"wmplayer.exe" : path.c_str(), NULL, NULL, SW_SHOW);
}