mirror of
https://github.com/chibicitiberiu/rainmeter-studio.git
synced 2024-02-24 04:33:31 +00:00
d633f4b586
- Added preliminary support to display lyrics (PlayerType=LYRICS) - Applied r825 fix on TrackChangeAction= (sends bang to correct window without the need of #CURRENTCONFIG#) - Some refactoring
547 lines
9.7 KiB
C++
547 lines
9.7 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "StdAfx.h"
|
|
#include "PlayerITunes.h"
|
|
|
|
CPlayer* CPlayerITunes::c_Player = NULL;
|
|
|
|
/*
|
|
** CEventHandler
|
|
**
|
|
** Constructor.
|
|
**
|
|
*/
|
|
CPlayerITunes::CEventHandler::CEventHandler(CPlayerITunes* player) :
|
|
m_Player(player),
|
|
m_RefCount(),
|
|
m_ConnectionPoint(),
|
|
m_ConnectionCookie()
|
|
{
|
|
IConnectionPointContainer* icpc;
|
|
m_Player->m_iTunes->QueryInterface(IID_IConnectionPointContainer, (void**)&icpc);
|
|
icpc->FindConnectionPoint(DIID__IiTunesEvents, &m_ConnectionPoint);
|
|
m_ConnectionPoint->Advise(this, &m_ConnectionCookie);
|
|
icpc->Release();
|
|
}
|
|
|
|
/*
|
|
** ~CEventHandler
|
|
**
|
|
** Destructor.
|
|
**
|
|
*/
|
|
CPlayerITunes::CEventHandler::~CEventHandler()
|
|
{
|
|
if (m_ConnectionPoint)
|
|
{
|
|
m_ConnectionPoint->Unadvise(m_ConnectionCookie);
|
|
m_ConnectionPoint->Release();
|
|
}
|
|
}
|
|
|
|
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()
|
|
{
|
|
return ++m_RefCount;
|
|
}
|
|
|
|
ULONG STDMETHODCALLTYPE CPlayerITunes::CEventHandler::Release()
|
|
{
|
|
return --m_RefCount;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CPlayerITunes::CEventHandler::Invoke(DISPID dispidMember, REFIID, LCID, WORD, DISPPARAMS* dispParams, VARIANT*, EXCEPINFO*, UINT*)
|
|
{
|
|
switch (dispidMember)
|
|
{
|
|
case ITEventPlayerPlay:
|
|
m_Player->OnStateChange(true);
|
|
m_Player->OnTrackChange();
|
|
break;
|
|
|
|
case ITEventPlayerStop:
|
|
m_Player->OnStateChange(false);
|
|
break;
|
|
|
|
case ITEventPlayerPlayingTrackChanged:
|
|
m_Player->OnTrackChange();
|
|
break;
|
|
|
|
case ITEventSoundVolumeChanged:
|
|
m_Player->OnVolumeChange(dispParams->rgvarg[0].intVal);
|
|
break;
|
|
|
|
case ITEventAboutToPromptUserToQuit:
|
|
m_Player->m_UserQuitPrompt = true;
|
|
m_Player->Uninitialize();
|
|
break;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
** CPlayerITunes
|
|
**
|
|
** Constructor.
|
|
**
|
|
*/
|
|
CPlayerITunes::CPlayerITunes() : CPlayer(),
|
|
m_UserQuitPrompt(false),
|
|
m_iTunes(),
|
|
m_iTunesEvent()
|
|
{
|
|
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
}
|
|
|
|
/*
|
|
** ~CPlayerITunes
|
|
**
|
|
** Destructor.
|
|
**
|
|
*/
|
|
CPlayerITunes::~CPlayerITunes()
|
|
{
|
|
c_Player = NULL;
|
|
Uninitialize();
|
|
CoUninitialize();
|
|
}
|
|
|
|
/*
|
|
** Create
|
|
**
|
|
** Creates a shared class object.
|
|
**
|
|
*/
|
|
CPlayer* CPlayerITunes::Create()
|
|
{
|
|
if (!c_Player)
|
|
{
|
|
c_Player = new CPlayerITunes();
|
|
}
|
|
|
|
return c_Player;
|
|
}
|
|
|
|
/*
|
|
** 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
|
|
m_iTunesEvent = new CEventHandler(this);
|
|
|
|
// 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();
|
|
delete m_iTunesEvent;
|
|
}
|
|
|
|
ClearData();
|
|
}
|
|
}
|
|
|
|
/*
|
|
** CheckWindow
|
|
**
|
|
** Try to find iTunes periodically.
|
|
**
|
|
*/
|
|
bool CPlayerITunes::CheckWindow()
|
|
{
|
|
static DWORD oldTime = 0;
|
|
DWORD time = GetTickCount();
|
|
|
|
if (time - oldTime > 5000)
|
|
{
|
|
oldTime = time;
|
|
|
|
HWND wnd = FindWindow(L"iTunes", L"iTunes");
|
|
if (wnd)
|
|
{
|
|
if (!m_UserQuitPrompt)
|
|
{
|
|
Initialize();
|
|
}
|
|
}
|
|
else if (m_UserQuitPrompt)
|
|
{
|
|
m_UserQuitPrompt = false;
|
|
}
|
|
}
|
|
|
|
return m_Initialized;
|
|
}
|
|
|
|
/*
|
|
** UpdateData
|
|
**
|
|
** Called during each update of the main measure.
|
|
**
|
|
*/
|
|
void CPlayerITunes::UpdateData()
|
|
{
|
|
if ((m_Initialized || CheckWindow()) && m_State != PLAYER_STOPPED)
|
|
{
|
|
long position;
|
|
m_iTunes->get_PlayerPosition(&position);
|
|
m_Position = (UINT)position;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** OnTrackChange
|
|
**
|
|
** Called by iTunes event handler on track change.
|
|
**
|
|
*/
|
|
void CPlayerITunes::OnTrackChange()
|
|
{
|
|
IITTrack* track;
|
|
HRESULT hr = m_iTunes->get_CurrentTrack(&track);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
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;
|
|
|
|
// Rating is 0 - 100, divide to 0 - 5
|
|
track->get_Rating(&tmpVal);
|
|
tmpVal /= 20L;
|
|
m_Rating = (UINT)tmpVal;
|
|
|
|
IITFileOrCDTrack* file;
|
|
hr = track->QueryInterface(&file);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
file->get_Location(&tmpStr);
|
|
file->Release();
|
|
if (tmpStr && wcscmp(tmpStr, m_FilePath.c_str()) != 0)
|
|
{
|
|
++m_TrackCount;
|
|
m_FilePath = tmpStr;
|
|
|
|
if (m_HasCoverMeasure)
|
|
{
|
|
m_CoverPath = GetCacheFile();
|
|
if (!CCover::GetCached(m_CoverPath))
|
|
{
|
|
// Art not in cache, check for embedded art through iTunes interface
|
|
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 = m_CoverPath.c_str();
|
|
hr = artwork->SaveArtworkToFile(tmpStr);
|
|
if (FAILED(hr))
|
|
{
|
|
m_CoverPath.clear();
|
|
}
|
|
|
|
artwork->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_CoverPath.clear();
|
|
}
|
|
|
|
artworkCollection->Release();
|
|
}
|
|
else
|
|
{
|
|
m_CoverPath.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_HasLyricsMeasure)
|
|
{
|
|
FindLyrics();
|
|
}
|
|
}
|
|
}
|
|
|
|
track->Release();
|
|
}
|
|
else
|
|
{
|
|
ClearData();
|
|
}
|
|
}
|
|
|
|
/*
|
|
** 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;
|
|
}
|
|
|
|
/*
|
|
** Pause
|
|
**
|
|
** Handles the Pause bang.
|
|
**
|
|
*/
|
|
void CPlayerITunes::Pause()
|
|
{
|
|
m_iTunes->Pause();
|
|
}
|
|
|
|
/*
|
|
** Play
|
|
**
|
|
** Handles the Play bang.
|
|
**
|
|
*/
|
|
void CPlayerITunes::Play()
|
|
{
|
|
m_iTunes->Play();
|
|
}
|
|
|
|
/*
|
|
** Stop
|
|
**
|
|
** Handles the Stop bang.
|
|
**
|
|
*/
|
|
void CPlayerITunes::Stop()
|
|
{
|
|
m_iTunes->Stop();
|
|
}
|
|
|
|
/*
|
|
** Next
|
|
**
|
|
** Handles the Next bang.
|
|
**
|
|
*/
|
|
void CPlayerITunes::Next()
|
|
{
|
|
m_iTunes->NextTrack();
|
|
}
|
|
|
|
/*
|
|
** Previous
|
|
**
|
|
** Handles the Previous bang.
|
|
**
|
|
*/
|
|
void CPlayerITunes::Previous()
|
|
{
|
|
m_iTunes->PreviousTrack();
|
|
}
|
|
|
|
/*
|
|
** SetPosition
|
|
**
|
|
** Handles the SetPosition bang.
|
|
**
|
|
*/
|
|
void CPlayerITunes::SetPosition(int position)
|
|
{
|
|
m_iTunes->put_PlayerPosition((long)position);
|
|
}
|
|
|
|
/*
|
|
** SetRating
|
|
**
|
|
** Handles the SetRating bang.
|
|
**
|
|
*/
|
|
void CPlayerITunes::SetRating(int rating)
|
|
{
|
|
IITTrack* track;
|
|
HRESULT hr = m_iTunes->get_CurrentTrack(&track);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
rating *= 20;
|
|
track->put_Rating((long)rating);
|
|
track->Release();
|
|
}
|
|
}
|
|
|
|
/*
|
|
** SetVolume
|
|
**
|
|
** Handles the SetVolume bang.
|
|
**
|
|
*/
|
|
void CPlayerITunes::SetVolume(int volume)
|
|
{
|
|
m_iTunes->put_SoundVolume((long)volume);
|
|
}
|
|
|
|
/*
|
|
** ClosePlayer
|
|
**
|
|
** Handles the ClosePlayer bang.
|
|
**
|
|
*/
|
|
void CPlayerITunes::ClosePlayer()
|
|
{
|
|
m_UserQuitPrompt = true;
|
|
m_iTunes->Quit();
|
|
Uninitialize();
|
|
}
|
|
|
|
/*
|
|
** OpenPlayer
|
|
**
|
|
** Handles the OpenPlayer bang.
|
|
**
|
|
*/
|
|
void CPlayerITunes::OpenPlayer(std::wstring& path)
|
|
{
|
|
ShellExecute(NULL, L"open", path.empty() ? L"iTunes.exe" : path.c_str(), NULL, NULL, SW_SHOW);
|
|
}
|