mirror of
https://github.com/chibicitiberiu/rainmeter-studio.git
synced 2024-02-24 04:33:31 +00:00
Committing NowPlaying plugin. Because it requires VS2010 Pro to compile, it is not built by default.
This commit is contained in:
587
Plugins/PluginNowPlaying/PlayerITunes.cpp
Normal file
587
Plugins/PluginNowPlaying/PlayerITunes.cpp
Normal file
@ -0,0 +1,587 @@
|
||||
/*
|
||||
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "PlayerITunes.h"
|
||||
|
||||
extern std::wstring g_CachePath;
|
||||
|
||||
/*
|
||||
** CEventHandler
|
||||
**
|
||||
** Constructor.
|
||||
**
|
||||
*/
|
||||
CPlayerITunes::CEventHandler::CEventHandler(CPlayerITunes* player) :
|
||||
m_iTunes(player),
|
||||
m_TypeInfo(),
|
||||
m_RefCount()
|
||||
{
|
||||
ITypeLib* pITypeLib = NULL;
|
||||
HRESULT hr = LoadRegTypeLib(LIBID_iTunesLib, 1, 5, 0x00, &pITypeLib);
|
||||
|
||||
// Get type information for the interface of the object.
|
||||
hr = pITypeLib->GetTypeInfoOfGuid(DIID__IiTunesEvents, &m_TypeInfo);
|
||||
pITypeLib->Release();
|
||||
}
|
||||
|
||||
/*
|
||||
** ~CEventHandler
|
||||
**
|
||||
** Destructor.
|
||||
**
|
||||
*/
|
||||
CPlayerITunes::CEventHandler::~CEventHandler()
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CPlayerITunes::CEventHandler::QueryInterface(REFIID iid, void** ppvObject)
|
||||
{
|
||||
if (iid == IID_IUnknown || iid == IID_IUnknown || iid == DIID__IiTunesEvents)
|
||||
{
|
||||
++m_RefCount;
|
||||
*ppvObject = this;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE CPlayerITunes::CEventHandler::AddRef()
|
||||
{
|
||||
InterlockedIncrement(&m_RefCount);
|
||||
return m_RefCount;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE CPlayerITunes::CEventHandler::Release()
|
||||
{
|
||||
InterlockedDecrement(&m_RefCount);
|
||||
if (m_RefCount == 0)
|
||||
{
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
return m_RefCount;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CPlayerITunes::CEventHandler::Invoke(DISPID dispidMember, REFIID, LCID, WORD, DISPPARAMS* dispParams, VARIANT*, EXCEPINFO*, UINT*)
|
||||
{
|
||||
switch (dispidMember)
|
||||
{
|
||||
case ITEventPlayerPlay:
|
||||
m_iTunes->OnStateChange(true);
|
||||
m_iTunes->OnTrackChange();
|
||||
break;
|
||||
|
||||
case ITEventPlayerStop:
|
||||
m_iTunes->OnStateChange(false);
|
||||
break;
|
||||
|
||||
case ITEventPlayerPlayingTrackChanged:
|
||||
m_iTunes->OnTrackChange();
|
||||
break;
|
||||
|
||||
case ITEventSoundVolumeChanged:
|
||||
m_iTunes->OnVolumeChange(dispParams->rgvarg[0].intVal);
|
||||
break;
|
||||
|
||||
case ITEventAboutToPromptUserToQuit:
|
||||
m_iTunes->m_UserQuitPrompt = true;
|
||||
m_iTunes->Uninitialize();
|
||||
break;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** CPlayerITunes
|
||||
**
|
||||
** Constructor.
|
||||
**
|
||||
*/
|
||||
CPlayerITunes::CPlayerITunes() :
|
||||
m_Initialized(false),
|
||||
m_UserQuitPrompt(false),
|
||||
m_HasCoverMeasure(false),
|
||||
m_Window(),
|
||||
m_iTunes(),
|
||||
m_iTunesEvent(),
|
||||
m_ConnectionPoint(),
|
||||
m_ConnectionCookie()
|
||||
{
|
||||
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
||||
}
|
||||
|
||||
/*
|
||||
** ~CPlayerITunes
|
||||
**
|
||||
** Destructor.
|
||||
**
|
||||
*/
|
||||
CPlayerITunes::~CPlayerITunes()
|
||||
{
|
||||
Uninitialize();
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
/*
|
||||
** AddInstance
|
||||
**
|
||||
** Called during initialization of each measure.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::AddInstance(MEASURETYPE type)
|
||||
{
|
||||
++m_InstanceCount;
|
||||
|
||||
if (type == MEASURE_COVER)
|
||||
{
|
||||
m_HasCoverMeasure = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** RemoveInstance
|
||||
**
|
||||
** Called during destruction of each measure.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::RemoveInstance()
|
||||
{
|
||||
if (--m_InstanceCount == 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize
|
||||
**
|
||||
** Initialize iTunes COM interface and event handler.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::Initialize()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
HRESULT hr = CoCreateInstance(CLSID_iTunesApp, NULL, CLSCTX_LOCAL_SERVER, IID_IiTunes, (PVOID*)&m_iTunes);
|
||||
|
||||
if (hr == CO_E_SERVER_EXEC_FAILURE)
|
||||
{
|
||||
// This seems to happen if there is a modal dialog being shown in iTunes
|
||||
// or some other delay has occurred. Retrying should do the trick.
|
||||
continue;
|
||||
}
|
||||
else if (hr != S_OK)
|
||||
{
|
||||
// Failed to get hold of iTunes instance via COM
|
||||
m_iTunes = NULL;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_iTunes)
|
||||
{
|
||||
m_Initialized = true;
|
||||
|
||||
// Set up event handler
|
||||
IConnectionPointContainer* icpc;
|
||||
m_iTunes->QueryInterface(IID_IConnectionPointContainer, (void **)&icpc);
|
||||
icpc->FindConnectionPoint(DIID__IiTunesEvents, &m_ConnectionPoint);
|
||||
icpc->Release();
|
||||
m_iTunesEvent = new CEventHandler(this);
|
||||
m_ConnectionPoint->Advise(m_iTunesEvent, &m_ConnectionCookie);
|
||||
|
||||
// Try getting track info and player state
|
||||
ITPlayerState state;
|
||||
if (SUCCEEDED(m_iTunes->get_PlayerState(&state)))
|
||||
{
|
||||
if (state == ITPlayerStateStopped)
|
||||
{
|
||||
// Determine if paused of stopped
|
||||
long position;
|
||||
m_iTunes->get_PlayerPosition(&position);
|
||||
|
||||
if (position != 0)
|
||||
{
|
||||
m_State = PLAYER_PAUSED;
|
||||
}
|
||||
}
|
||||
else if (state == ITPlayerStatePlaying)
|
||||
{
|
||||
m_State = PLAYER_PLAYING;
|
||||
}
|
||||
|
||||
if (m_State != PLAYER_STOPPED)
|
||||
{
|
||||
OnTrackChange();
|
||||
}
|
||||
|
||||
long volume;
|
||||
m_iTunes->get_SoundVolume(&volume);
|
||||
m_Volume = (UINT)volume;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Initialized = false;
|
||||
LSLog(LOG_ERROR, L"Rainmeter", L"NowPlayingPlugin: Failed to get hold of iTunes instance via COM.");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Uninitialize
|
||||
**
|
||||
** Close iTunes COM interface.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::Uninitialize()
|
||||
{
|
||||
if (m_Initialized)
|
||||
{
|
||||
m_Initialized = false;
|
||||
|
||||
if (m_iTunes)
|
||||
{
|
||||
m_iTunes->Release();
|
||||
m_iTunesEvent->Release();
|
||||
}
|
||||
|
||||
if (m_ConnectionPoint)
|
||||
{
|
||||
m_ConnectionPoint->Unadvise(m_ConnectionCookie);
|
||||
m_ConnectionPoint->Release();
|
||||
}
|
||||
|
||||
ClearInfo();
|
||||
}
|
||||
}
|
||||
|
||||
bool CPlayerITunes::CheckActive()
|
||||
{
|
||||
static DWORD oldTime = 0;
|
||||
DWORD time = GetTickCount();
|
||||
|
||||
if (time - oldTime > 5000)
|
||||
{
|
||||
oldTime = time;
|
||||
m_Window = FindWindow(L"iTunes", L"iTunes");
|
||||
return m_Window ? true : false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
** UpdateData
|
||||
**
|
||||
** Called during each update of the main measure.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::UpdateData()
|
||||
{
|
||||
if (m_Initialized)
|
||||
{
|
||||
if (m_TrackChanged)
|
||||
{
|
||||
ExecuteTrackChangeAction();
|
||||
m_TrackChanged = false;
|
||||
}
|
||||
|
||||
long position;
|
||||
m_iTunes->get_PlayerPosition(&position);
|
||||
m_Position = (UINT)position;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CheckActive())
|
||||
{
|
||||
if (!m_UserQuitPrompt)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
else if (m_UserQuitPrompt)
|
||||
{
|
||||
m_UserQuitPrompt = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** OnTrackChange
|
||||
**
|
||||
** Called by iTunes event handler on track change.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::OnTrackChange()
|
||||
{
|
||||
IITTrack* track;
|
||||
HRESULT hr = m_iTunes->get_CurrentTrack(&track);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
m_TrackChanged = true;
|
||||
CComBSTR tmpStr;
|
||||
long tmpVal;
|
||||
|
||||
// Get metadata
|
||||
track->get_Artist(&tmpStr);
|
||||
tmpStr ? (m_Artist = tmpStr) : m_Artist.clear();
|
||||
|
||||
track->get_Name(&tmpStr);
|
||||
tmpStr ? (m_Title = tmpStr) : m_Title.clear();
|
||||
|
||||
track->get_Album(&tmpStr);
|
||||
tmpStr ? (m_Album = tmpStr) : m_Album.clear();
|
||||
|
||||
track->get_Duration(&tmpVal);
|
||||
m_Duration = (UINT)tmpVal;
|
||||
|
||||
track->get_Rating(&tmpVal);
|
||||
tmpVal /= 20L;
|
||||
m_Rating = (UINT)tmpVal;
|
||||
|
||||
if (m_HasCoverMeasure)
|
||||
{
|
||||
// Check if MP3 file contains embedded art
|
||||
std::wstring cover = CreateCoverArtPath();
|
||||
if (_waccess(cover.c_str(), 0) == 0)
|
||||
{
|
||||
// Cover is in cache, lets use the that
|
||||
m_CoverPath = cover;
|
||||
}
|
||||
else
|
||||
{
|
||||
IITArtworkCollection* artworkCollection;
|
||||
hr = track->get_Artwork(&artworkCollection);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
long count;
|
||||
artworkCollection->get_Count(&count);
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
IITArtwork* artwork;
|
||||
hr = artworkCollection->get_Item(1, &artwork);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
tmpStr = cover.c_str();
|
||||
hr = artwork->SaveArtworkToFile(tmpStr);
|
||||
SUCCEEDED(hr) ? m_CoverPath = cover : m_CoverPath.clear();
|
||||
|
||||
artwork->Release();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_CoverPath.clear();
|
||||
}
|
||||
|
||||
artworkCollection->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
track->Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearInfo();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** OnStateChange
|
||||
**
|
||||
** Called by iTunes event handler on player state change.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::OnStateChange(bool playing)
|
||||
{
|
||||
if (playing)
|
||||
{
|
||||
m_State = PLAYER_PLAYING;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Guess if paused or stopped from track time
|
||||
m_State = (m_Position == 0) ? PLAYER_STOPPED : PLAYER_PAUSED;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** OnVolumeChange
|
||||
**
|
||||
** Called by iTunes event handler on volume change.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::OnVolumeChange(int volume)
|
||||
{
|
||||
m_Volume = volume;
|
||||
}
|
||||
|
||||
/*
|
||||
** PlayPause
|
||||
**
|
||||
** Handles the PlayPause bang.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::PlayPause()
|
||||
{
|
||||
if (m_Initialized)
|
||||
{
|
||||
m_iTunes->PlayPause();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Stop
|
||||
**
|
||||
** Handles the Stop bang.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::Stop()
|
||||
{
|
||||
if (m_Initialized)
|
||||
{
|
||||
m_iTunes->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Next
|
||||
**
|
||||
** Handles the Next bang.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::Next()
|
||||
{
|
||||
if (m_Initialized)
|
||||
{
|
||||
m_iTunes->NextTrack();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Previous
|
||||
**
|
||||
** Handles the Previous bang.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::Previous()
|
||||
{
|
||||
if (m_Initialized)
|
||||
{
|
||||
m_iTunes->PreviousTrack();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** SetRating
|
||||
**
|
||||
** Handles the SetRating bang.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::SetRating(int rating)
|
||||
{
|
||||
if (m_Initialized)
|
||||
{
|
||||
rating *= 20;
|
||||
IITTrack* track;
|
||||
HRESULT hr = m_iTunes->get_CurrentTrack(&track);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
track->put_Rating((long)rating);
|
||||
track->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** SetVolume
|
||||
**
|
||||
** Handles the SetVolume bang.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::SetVolume(int volume)
|
||||
{
|
||||
if (m_Initialized)
|
||||
{
|
||||
m_iTunes->put_SoundVolume((long)volume);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** ChangeVolume
|
||||
**
|
||||
** Handles the ChangeVolume bang.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::ChangeVolume(int volume)
|
||||
{
|
||||
if (m_Initialized)
|
||||
{
|
||||
int newVolume = m_Volume;
|
||||
newVolume += volume;
|
||||
m_iTunes->put_SoundVolume(newVolume);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** ClosePlayer
|
||||
**
|
||||
** Handles the ClosePlayer bang.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::ClosePlayer()
|
||||
{
|
||||
if (m_Initialized)
|
||||
{
|
||||
m_Initialized = false;
|
||||
m_iTunes->Quit();
|
||||
ClearInfo();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** OpenPlayer
|
||||
**
|
||||
** Handles the OpenPlayer bang.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::OpenPlayer()
|
||||
{
|
||||
ShellExecute(NULL, L"open", m_PlayerPath.empty() ? L"iTunes.exe" : m_PlayerPath.c_str(), NULL, NULL, SW_SHOW);
|
||||
}
|
||||
|
||||
/*
|
||||
** TogglePlayer
|
||||
**
|
||||
** Handles the TogglePlayer bang.
|
||||
**
|
||||
*/
|
||||
void CPlayerITunes::TogglePlayer()
|
||||
{
|
||||
m_Initialized ? ClosePlayer() : OpenPlayer();
|
||||
}
|
Reference in New Issue
Block a user