NowPlayingPlugin:

- 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
This commit is contained in:
Birunthan Mohanathas 2011-06-19 14:58:48 +00:00
parent 6aa004eb22
commit d633f4b586
29 changed files with 1181 additions and 329 deletions

View File

@ -19,76 +19,25 @@
#include "StdAfx.h"
#include "Cover.h"
extern std::wstring g_CachePath;
/*
** GetCover
**
** Default implementation for getting cover.
**
*/
void CCover::GetCover(const std::wstring& artist, const std::wstring& title, const std::wstring& file, std::wstring& target)
{
if (!GetCachedCover(artist, title, target))
{
TagLib::FileRef fr(file.c_str());
if (fr.isNull() || !fr.tag() || !GetEmbeddedCover(fr, target))
{
std::wstring trackFolder = GetFileFolder(file);
if (!GetLocalCover(L"cover", trackFolder, target) &&
!GetLocalCover(L"folder", trackFolder, target))
{
// Nothing found
target.clear();
}
}
}
}
/*
** GetCachedArt
** GetCached
**
** Checks if cover art is in cache.
**
*/
bool CCover::GetCachedCover(const std::wstring& artist, const std::wstring& title, std::wstring& target)
bool CCover::GetCached(std::wstring& path)
{
target = g_CachePath;
if (artist.empty() || title.empty())
{
target += L"temp.art";
}
else
{
// Otherwise, save it as "Artist - Title.art"
std::wstring name = artist;
name += L" - ";
name += title;
// Replace reserved chars with _
std::wstring::size_type pos = 0;
while ((pos = name.find_first_of(L"\\/:*?\"<>|", pos)) != std::wstring::npos) name[pos] = L'_';
target += name;
target += L".art";
if (_waccess(target.c_str(), 0) == 0)
{
// Art found in cache
return true;
}
}
return false;
path += L".art";
return (_waccess(path.c_str(), 0) == 0) ? true : false;
}
/*
** GetLocalArt
** GetLocal
**
** Attemps to find local cover art in various formats.
**
*/
bool CCover::GetLocalCover(std::wstring filename, const std::wstring& folder, std::wstring& target)
bool CCover::GetLocal(std::wstring filename, const std::wstring& folder, std::wstring& target)
{
std::wstring testPath = folder;
testPath += filename;
@ -117,12 +66,12 @@ bool CCover::GetLocalCover(std::wstring filename, const std::wstring& folder, st
}
/*
** GetEmbeddedArt
** GetEmbedded
**
** Attempts to extract cover art from audio files.
**
*/
bool CCover::GetEmbeddedCover(const TagLib::FileRef& fr, std::wstring& target)
bool CCover::GetEmbedded(const TagLib::FileRef& fr, std::wstring& target)
{
bool found = false;

View File

@ -19,6 +19,8 @@
#ifndef __COVER_H__
#define __COVER_H__
// TagLib
#include "fileref.h"
#include "apefile.h"
#include "apetag.h"
#include "asffile.h"
@ -40,10 +42,9 @@
class CCover
{
public:
static void GetCover(const std::wstring& artist, const std::wstring& title, const std::wstring& file, std::wstring& target);
static bool GetCachedCover(const std::wstring& artist, const std::wstring& title, std::wstring& target);
static bool GetLocalCover(std::wstring filename, const std::wstring& folder, std::wstring& target);
static bool GetEmbeddedCover(const TagLib::FileRef& fr, std::wstring& target);
static bool GetCached(std::wstring& path);
static bool GetLocal(std::wstring filename, const std::wstring& folder, std::wstring& target);
static bool GetEmbedded(const TagLib::FileRef& fr, std::wstring& target);
static std::wstring GetFileFolder(const std::wstring& file);
private:

View File

@ -0,0 +1,281 @@
/*
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 "Internet.h"
HINTERNET CInternet::c_NetHandle = NULL;
/*
** Initialize
**
** Initialize internet handle and crtical section.
**
*/
void CInternet::Initialize()
{
c_NetHandle = InternetOpen(L"Rainmeter NowPlaying.dll",
INTERNET_OPEN_TYPE_PRECONFIG,
NULL,
NULL,
0);
if (!c_NetHandle)
{
LSLog(LOG_ERROR, L"Rainmeter", L"NowPlayingPlugin: Unable to open net handle.");
}
}
/*
** Finalize
**
** Close handles and delete critical section.
**
*/
void CInternet::Finalize()
{
if (c_NetHandle) InternetCloseHandle(c_NetHandle);
}
/*
** DownloadUrl
**
** Downloads given url and returns it as a string.
**
*/
std::wstring CInternet::DownloadUrl(const std::wstring& url, int codepage)
{
std::wstring result;
DWORD flags = INTERNET_FLAG_RESYNCHRONIZE;
HINTERNET hUrlDump = InternetOpenUrl(c_NetHandle, url.c_str(), NULL, NULL, flags, 0);
if (!hUrlDump)
{
LSLog(LOG_DEBUG, L"Rainmeter", L"NowPlayingPlguin: Unable to open internet file.");
return result;
}
// Allocate the buffer.
const int CHUNK_SIZE = 8192;
BYTE* lpData = new BYTE[CHUNK_SIZE];
BYTE* lpOutPut;
BYTE* lpHolding = NULL;
int nCounter = 1;
int nBufferSize;
DWORD dwDataSize = 0;
DWORD dwSize = 0;
do
{
// Read the data.
if (!InternetReadFile(hUrlDump, (LPVOID)lpData, CHUNK_SIZE, &dwSize))
{
LSLog(LOG_DEBUG, L"Rainmeter", L"NowPlayingPlguin: Unable to read internet file.");
break;
}
else
{
// Check if all of the data has been read. This should
// never get called on the first time through the loop.
if (dwSize == 0)
{
break;
}
// Determine the buffer size to hold the new data and the data
// already written (if any).
nBufferSize = dwDataSize + dwSize;
// Allocate the output buffer.
lpOutPut = new BYTE[nBufferSize + 2];
// Make sure the buffer is not the initial buffer.
if (lpHolding != NULL)
{
// Copy the data in the holding buffer.
memcpy(lpOutPut, lpHolding, dwDataSize);
// Delete the old buffer
delete [] lpHolding;
lpHolding = lpOutPut;
lpOutPut = lpOutPut + dwDataSize;
}
else
{
lpHolding = lpOutPut;
}
// Copy the data buffer.
memcpy(lpOutPut, lpData, dwSize);
dwDataSize += dwSize;
// End with double null
lpOutPut[dwSize] = 0;
lpOutPut[dwSize + 1] = 0;
// Increment the number of buffers read.
++nCounter;
// Clear the buffer
memset(lpData, 0, CHUNK_SIZE);
}
} while (true);
InternetCloseHandle(hUrlDump);
delete [] lpData;
if (lpHolding)
{
result = ConvertToWide((LPCSTR)lpHolding, codepage);
delete [] lpHolding;
}
return result;
}
/*
** EncodeUrl
**
** Encode reserved characters.
**
*/
std::wstring CInternet::EncodeUrl(const std::wstring& url)
{
// Based on http://www.zedwood.com/article/111/cpp-urlencode-function
static const std::wstring url_chars = L" !*'();:@&=+$,/?#[]";
std::wstring ret;
for (size_t i = 0, max = url.length(); i < max; ++i)
{
if (url_chars.find_first_of(url[i]) != std::string::npos)
{
// If reserved character
ret.append(L"%");
WCHAR buffer[3];
_snwprintf_s(buffer, 3, L"%.2X", url[i]);
ret.append(buffer);
}
else
{
ret.push_back(url[i]);
}
}
return ret;
}
/*
** DecodeReferences
**
** Decodes numeric references.
**
*/
void CInternet::DecodeReferences(std::wstring& str)
{
// From WebParser.cpp
std::wstring::size_type start = 0;
while ((start = str.find(L'&', start)) != std::wstring::npos)
{
std::wstring::size_type end, pos;
if ((end = str.find(L';', start)) == std::wstring::npos) break;
pos = start + 1;
if (pos == end) // &; - skip
{
start = end + 1;
continue;
}
else if ((end - pos) > 10) // name (or number) is too long
{
++start;
continue;
}
if (str[pos] == L'#') // Numeric character reference
{
if (++pos == end) // &#; - skip
{
start = end + 1;
continue;
}
int base;
if (str[pos] == L'x' || str[pos] == L'X')
{
if (++pos == end) // &#x; or &#X; - skip
{
start = end + 1;
continue;
}
base = 16;
}
else
{
base = 10;
}
std::wstring num(str, pos, end - pos);
WCHAR* pch = NULL;
errno = 0;
long ch = wcstol(num.c_str(), &pch, base);
if (pch == NULL || *pch != L'\0' || errno == ERANGE || ch <= 0 || ch >= 0xFFFE) // invalid character
{
start = pos;
continue;
}
str.replace(start, end - start + 1, 1, (WCHAR)ch);
++start;
}
else // Character entity reference
{
start = end + 1;
continue;
}
}
}
/*
** ConvertToWide
**
** Convert from multibyte ti wide string.
**
*/
std::wstring CInternet::ConvertToWide(LPCSTR str, int codepage)
{
std::wstring szWide;
if (str && *str)
{
int strLen = (int)strlen(str) + 1;
int bufLen = MultiByteToWideChar(codepage, 0, str, strLen, NULL, 0);
if (bufLen > 0)
{
WCHAR* wideSz = new WCHAR[bufLen];
wideSz[0] = 0;
MultiByteToWideChar(codepage, 0, str, strLen, wideSz, bufLen);
szWide = wideSz;
delete [] wideSz;
}
}
return szWide;
}

View File

@ -0,0 +1,37 @@
/*
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.
*/
#ifndef __INTERNET_H__
#define __INTERNET_H__
class CInternet
{
public:
static void Initialize();
static void Finalize();
static std::wstring DownloadUrl(const std::wstring& url, int codepage);
static std::wstring EncodeUrl(const std::wstring& url);
static void DecodeReferences(std::wstring& str);
static std::wstring ConvertToWide(LPCSTR str, int codepage);
private:
static HINTERNET c_NetHandle;
};
#endif

View File

@ -0,0 +1,204 @@
/*
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 "Player.h"
#include "Internet.h"
#include "Lyrics.h"
/*
** GetFromInternet
**
** Download lyrics from various serivces.
**
*/
bool CLyrics::GetFromInternet(const std::wstring& artist, const std::wstring& title, std::wstring& out)
{
std::wstring encArtist = CInternet::EncodeUrl(artist);
std::wstring encTitle = CInternet::EncodeUrl(title);
bool found = GetFromWikia(encArtist, encTitle, out) ||
GetFromLYRDB(encArtist, encTitle, out) ||
GetFromLetras(encArtist, encTitle, out);
return found;
}
/*
** GetFromWikia
**
** Download lyrics from LyricWiki.
**
*/
bool CLyrics::GetFromWikia(const std::wstring& artist, const std::wstring& title, std::wstring& data)
{
bool ret = false;
std::wstring url = L"http://lyrics.wikia.com/api.php?func=getSong&fmt=json&artist=";
url += artist;
url += L"&song=";
url += title;
data = CInternet::DownloadUrl(url, CP_UTF8);
if (!data.empty())
{
// First we get the URL to the actual wiki page
std::wstring::size_type pos = data.find(L"http://");
if (pos != std::wstring::npos)
{
data.erase(0, pos);
pos = data.find(L"'");
url = data.substr(0, pos);
// Fetch the wiki page
data = CInternet::DownloadUrl(url, CP_UTF8);
if (!data.empty())
{
pos = data.find(L"'lyricbox'");
pos = data.find(L"&#", pos);
if (pos != std::wstring::npos)
{
// Get and decode lyrics
data.erase(0, pos);
pos = data.find(L"<!");
data.resize(pos);
CInternet::DecodeReferences(data);
pos = data.find(L"[...]");
if (pos != std::wstring::npos)
{
// Skip incomplete lyrics
return ret;
}
pos = data.find(L"<p>");
if (pos != std::wstring::npos)
{
// Skip unavailable lyrics
return ret;
}
while ((pos = data.find(L"<br />"), pos) != std::wstring::npos)
{
data.replace(pos, 6, L"\n");
}
// Get rid of all HTML tags
std::wstring::size_type len = 0;
while ((pos = data.find_first_of(L'<'), pos) != std::wstring::npos)
{
len = data.find_first_of(L'>', pos);
len -= pos;
data.erase(pos, ++len);
}
ret = true;
}
}
}
}
return ret;
}
/*
** GetFromLYRDB
**
** Download lyrics from LYRDB.
**
*/
bool CLyrics::GetFromLYRDB(const std::wstring& artist, const std::wstring& title, std::wstring& data)
{
bool ret = false;
std::wstring query = artist;
query += L"|";
query += title;
// LYRDB doesn't like apostrophes
std::wstring::size_type pos = 0;
while ((pos = query.find(L"%27", pos)) != std::wstring::npos)
{
query.erase(pos, 3);
}
std::wstring url = L"http://webservices.lyrdb.com/lookup.php?q=";
url += query;
url += L"&for=match&agent=RainmeterNowPlaying";
data = CInternet::DownloadUrl(url, CP_ACP);
if (!data.empty())
{
pos = data.find(L"\\");
if (pos != std::wstring::npos)
{
// Grab the first match
url = L"http://webservices.lyrdb.com/getlyr.php?q=";
url += data.substr(0, pos);
data = CInternet::DownloadUrl(url, CP_ACP);
if (!data.empty())
{
ret = true;
}
}
}
return ret;
}
/*
** GetFromLetras
**
** Download lyrics from Letras.
**
*/
bool CLyrics::GetFromLetras(const std::wstring& artist, const std::wstring& title, std::wstring& data)
{
bool ret = false;
std::wstring url = L"http://letras.terra.com.br/winamp.php?musica=";
url += title;
url += L"&artista=";
url += artist;
data = CInternet::DownloadUrl(url, CP_ACP);
if (!data.empty())
{
std::wstring::size_type pos = data.find(L"\"letra\"");
pos = data.find(L"<p>", pos);
if (pos != std::wstring::npos)
{
pos += 3;
data.erase(0, pos);
pos = data.find(L"</p>");
data.resize(pos);
CInternet::DecodeReferences(data);
while ((pos = data.find(L"<br/>"), pos) != std::wstring::npos)
{
data.erase(pos, 5);
}
ret = true;
}
}
return ret;
}

View File

@ -0,0 +1,33 @@
/*
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.
*/
#ifndef __LYRICS_H__
#define __LYRICS_H__
class CLyrics
{
public:
static bool GetFromInternet(const std::wstring& artist, const std::wstring& title, std::wstring& out);
private:
static bool GetFromLetras(const std::wstring& artist, const std::wstring& title, std::wstring& data);
static bool GetFromLYRDB(const std::wstring& artist, const std::wstring& title, std::wstring& data);
static bool GetFromWikia(const std::wstring& artist, const std::wstring& title, std::wstring& data);
};
#endif

View File

@ -19,6 +19,7 @@
#include "StdAfx.h"
#include "../../Library/DisableThreadLibraryCalls.h" // contains DllMain entry point
#include "NowPlaying.h"
#include "Internet.h"
#include "PlayerAIMP.h"
#include "PlayerCAD.h"
#include "PlayerFoobar.h"
@ -28,17 +29,7 @@
#include "PlayerWLM.h"
#include "PlayerWMP.h"
CPlayer* g_AIMP = NULL;
CPlayer* g_CAD = NULL;
CPlayer* g_Foobar = NULL;
CPlayer* g_iTunes = NULL;
CPlayer* g_Spotify = NULL;
CPlayer* g_Winamp = NULL;
CPlayer* g_WLM = NULL;
CPlayer* g_WMP = NULL;
static std::map<UINT, ChildMeasure*> g_Measures;
static bool g_DisableLeazingZero = false;
std::wstring g_CachePath;
std::wstring g_SettingsFile;
@ -71,6 +62,8 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
{
LSLog(LOG_ERROR, L"Rainmeter", L"NowPlayingPlugin: Unable to get path to Plugins.ini.");
}
CInternet::Initialize();
}
// Data is stored in two structs: ChildMeasure and ParentMeasure. ParentMeasure is created for measures
@ -125,75 +118,39 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
if (_wcsicmp(L"AIMP", str) == 0)
{
if (!g_AIMP)
{
g_AIMP = new CPlayerAIMP();
}
parent->player = g_AIMP;
parent->player = CPlayerAIMP::Create();
}
else if (_wcsicmp(L"CAD", str) == 0)
{
if (!g_CAD)
{
g_CAD = new CPlayerCAD();
}
parent->player = g_CAD;
parent->player = CPlayerCAD::Create();
}
else if (_wcsicmp(L"foobar2000", str) == 0)
{
if (!g_Foobar)
{
g_Foobar = new CPlayerFoobar();
}
parent->player = g_Foobar;
parent->player = CPlayerFoobar::Create();
}
else if (_wcsicmp(L"iTunes", str) == 0)
{
if (!g_iTunes)
{
g_iTunes = new CPlayerITunes();
}
parent->player = g_iTunes;
parent->player = CPlayerITunes::Create();
}
else if (_wcsicmp(L"MediaMonkey", str) == 0)
{
if (!g_Winamp)
{
g_Winamp = new CPlayerWinamp(WA_MEDIAMONKEY);
}
parent->player = g_Winamp;
parent->player = CPlayerWinamp::Create(WA_MEDIAMONKEY);
}
else if (_wcsicmp(L"Spotify", str) == 0)
{
if (!g_Spotify)
{
g_Spotify = new CPlayerSpotify();
}
parent->player = g_Spotify;
parent->player = CPlayerSpotify::Create();
}
else if (_wcsicmp(L"WinAmp", str) == 0)
{
if (!g_Winamp)
{
g_Winamp = new CPlayerWinamp(WA_WINAMP);
}
parent->player = g_Winamp;
parent->player = CPlayerWinamp::Create(WA_WINAMP);
}
else if (_wcsicmp(L"WLM", str) == 0)
{
if (!g_WLM)
{
g_WLM = new CPlayerWLM();
}
parent->player = g_WLM;
parent->player = CPlayerWLM::Create();
}
else if (_wcsicmp(L"WMP", str) == 0)
{
if (!g_WMP)
{
g_WMP = new CPlayerWMP();
}
parent->player = g_WMP;
parent->player = CPlayerWMP::Create();
}
else
{
@ -331,6 +288,11 @@ void Finalize(HMODULE instance, UINT id)
delete child;
g_Measures.erase(i);
if (g_Measures.empty())
{
CInternet::Finalize();
}
}
}
@ -359,8 +321,6 @@ UINT Update(UINT id)
parent->trackCount != player->GetTrackCount())
{
ExecuteCommand(parent->trackChangeAction, parent->window);
// TODO: First is true..
parent->trackCount = player->GetTrackCount();
}
}
@ -423,6 +383,9 @@ LPCTSTR GetString(UINT id, UINT flags)
case MEASURE_ALBUM:
return player->GetAlbum();
case MEASURE_LYRICS:
return player->GetLyrics();
case MEASURE_COVER:
return player->GetCoverPath();

View File

@ -27,7 +27,6 @@ struct ParentMeasure
UINT childCount;
UINT trackCount;
CPlayer* player;
HANDLE thread;
HWND window;
std::wstring name;
std::wstring iniFile;
@ -35,7 +34,7 @@ struct ParentMeasure
std::wstring playerPath;
bool disableLeadingZero;
ParentMeasure() : player(NULL), thread(NULL) {}
ParentMeasure() : player(NULL) {}
};
struct ChildMeasure

View File

@ -19,6 +19,8 @@
#include "StdAfx.h"
#include "Player.h"
extern std::wstring g_CachePath;
/*
** CPlayer
**
@ -36,8 +38,11 @@ CPlayer::CPlayer() :
m_Duration(),
m_Position(),
m_Rating(),
m_Volume()
m_Volume(),
m_CriticalSection(),
m_InternetThread()
{
InitializeCriticalSection(&m_CriticalSection);
}
/*
@ -48,6 +53,7 @@ CPlayer::CPlayer() :
*/
CPlayer::~CPlayer()
{
DeleteCriticalSection(&m_CriticalSection);
}
/*
@ -110,6 +116,131 @@ void CPlayer::UpdateMeasure()
}
}
/*
** GetCacheFile
**
** Creates escaped path to cache file (without extension)
**
*/
std::wstring CPlayer::GetCacheFile()
{
std::wstring path = g_CachePath;
if (m_Artist.empty() || m_Title.empty())
{
path += L"temp";
}
else
{
std::wstring name = m_Artist;
name += L" - ";
name += m_Title;
// Replace reserved chars with _
std::wstring::size_type pos = 0;
while ((pos = name.find_first_of(L"\\/:*?\"<>|", pos)) != std::wstring::npos) name[pos] = L'_';
path += name;
}
return path;
}
/*
** FindCover
**
** Default implementation for getting cover.
**
*/
void CPlayer::FindCover()
{
m_CoverPath = GetCacheFile();
if (!CCover::GetCached(m_CoverPath))
{
TagLib::FileRef fr(m_FilePath.c_str());
if (fr.isNull() || !fr.tag() || !CCover::GetEmbedded(fr, m_CoverPath))
{
std::wstring trackFolder = CCover::GetFileFolder(m_FilePath);
if (!CCover::GetLocal(L"cover", trackFolder, m_CoverPath) &&
!CCover::GetLocal(L"folder", trackFolder, m_CoverPath))
{
// Nothing found
m_CoverPath.clear();
}
}
}
}
/*
** FindLyrics
**
** Default implementation for getting lyrics.
**
*/
void CPlayer::FindLyrics()
{
if (TryEnterCriticalSection(&m_CriticalSection))
{
m_Lyrics.clear();
unsigned int id;
HANDLE thread = (HANDLE)_beginthreadex(NULL, 0, LyricsThreadProc, this, 0, &id);
if (thread)
{
m_InternetThread = thread;
}
else
{
LSLog(LOG_DEBUG, L"Rainmeter", L"NowPlayingPlugin: Failed to start lyrics thread.");
}
LeaveCriticalSection(&m_CriticalSection);
}
}
/*
** LyricsThreadProc
**
** Thread to download lyrics.
**
*/
unsigned __stdcall CPlayer::LyricsThreadProc(void* pParam)
{
CPlayer* player = (CPlayer*)pParam;
EnterCriticalSection(&player->m_CriticalSection);
std::wstring lyrics;
bool found;
while (true)
{
UINT beforeCount = player->GetTrackCount();
found = CLyrics::GetFromInternet(player->m_Artist, player->m_Title, lyrics);
UINT afterCount = player->GetTrackCount();
if (beforeCount == afterCount)
{
// We're on the same track
break;
}
// Track changed, fetch lyrics for it
}
if (found)
{
player->m_Lyrics = lyrics;
}
CloseHandle(player->m_InternetThread);
player->m_InternetThread = NULL;
LeaveCriticalSection(&player->m_CriticalSection);
return 0;
}
/*
** ClearData
**
@ -125,6 +256,7 @@ void CPlayer::ClearData()
m_Artist.clear();
m_Album.clear();
m_Title.clear();
m_Lyrics.clear();
m_FilePath.clear();
m_CoverPath.clear();
}

View File

@ -19,8 +19,11 @@
#ifndef __PLAYER_H__
#define __PLAYER_H__
#include "fileref.h"
#include "tag.h"
#include "Cover.h"
#include "Internet.h"
#include "Lyrics.h"
enum PLAYSTATE
{
@ -45,8 +48,7 @@ enum MEASURETYPE
MEASURE_FILE
};
class CPlayer :
public CCover
class CPlayer
{
public:
CPlayer();
@ -60,6 +62,10 @@ public:
bool IsInitialized() { return m_Initialized; }
UINT GetTrackCount() { return m_TrackCount; }
std::wstring GetCacheFile();
void FindCover();
void FindLyrics();
virtual void Pause() {}
virtual void Play() {}
@ -105,6 +111,12 @@ protected:
UINT m_Position; // Current position in seconds
UINT m_Rating; // Track rating from 0 to 100
UINT m_Volume; // Volume from 0 to 100
private:
static unsigned __stdcall LyricsThreadProc(void* pParam);
HANDLE m_InternetThread;
CRITICAL_SECTION m_CriticalSection;
};
#endif

View File

@ -21,8 +21,9 @@
#include "AIMP/aimp2_sdk.h"
#include "Winamp/wa_ipc.h"
extern CPlayer* g_AIMP;
CPlayer* CPlayerAIMP::c_Player = NULL;
// TODO
/*
** CPlayerAIMP
**
@ -30,10 +31,10 @@ extern CPlayer* g_AIMP;
**
*/
CPlayerAIMP::CPlayerAIMP() : CPlayer(),
m_FileMap(),
m_FileMapHandle(),
m_Window(),
m_WinampWindow()
m_WinampWindow(),
m_FileMap(),
m_FileMapHandle()
{
}
@ -45,74 +46,69 @@ CPlayerAIMP::CPlayerAIMP() : CPlayer(),
*/
CPlayerAIMP::~CPlayerAIMP()
{
g_AIMP = NULL;
c_Player = NULL;
if (m_FileMap) UnmapViewOfFile(m_FileMap);
if (m_FileMapHandle) CloseHandle(m_FileMapHandle);
}
/*
** Initialize
** Create
**
** Find AIMP window and mapped object.
** Creates a shared class object.
**
*/
bool CPlayerAIMP::Initialize()
CPlayer* CPlayerAIMP::Create()
{
m_Window = FindWindow(L"AIMP2_RemoteInfo", L"AIMP2_RemoteInfo");
m_WinampWindow = FindWindow(L"Winamp v1.x", NULL);
if (m_Window)
if (!c_Player)
{
m_FileMapHandle = OpenFileMapping(FILE_MAP_READ, FALSE, L"AIMP2_RemoteInfo");
if (!m_FileMapHandle)
{
LSLog(LOG_ERROR, L"Rainmeter", L"NowPlayingPlugin: AIMP - Unable to access mapping.");
return false;
}
m_FileMap = (LPVOID)MapViewOfFile(m_FileMapHandle, FILE_MAP_READ, 0, 0, 2048);
if (!m_FileMap)
{
LSLog(LOG_ERROR, L"Rainmeter", L"NowPlayingPlugin: AIMP - Unable to view mapping.");
return false;
}
return true;
c_Player = new CPlayerAIMP();
}
return false;
return c_Player;
}
bool CPlayerAIMP::CheckActive()
/*
** CheckWindow
**
** Try to find AIMP periodically.
**
*/
bool CPlayerAIMP::CheckWindow()
{
if (m_Window)
{
if (!IsWindow(m_Window))
{
m_Window = NULL;
m_WinampWindow = NULL;
if (m_FileMap) UnmapViewOfFile(m_FileMap);
if (m_FileMapHandle) CloseHandle(m_FileMapHandle);
ClearData();
return false;
}
return true;
}
else
{
static DWORD oldTime = 0;
DWORD time = GetTickCount();
static DWORD oldTime = 0;
DWORD time = GetTickCount();
// Try to find AIMP every 5 seconds
if (time - oldTime > 5000)
{
oldTime = time;
return Initialize();
}
// Try to find AIMP every 5 seconds
if (time - oldTime > 5000)
{
oldTime = time;
m_Window = FindWindow(L"AIMP2_RemoteInfo", L"AIMP2_RemoteInfo");
return false;
if (m_Window)
{
m_WinampWindow = FindWindow(L"Winamp v1.x", NULL);
m_FileMapHandle = OpenFileMapping(FILE_MAP_READ, FALSE, L"AIMP2_RemoteInfo");
if (m_FileMapHandle)
{
m_FileMap = (LPVOID)MapViewOfFile(m_FileMapHandle, FILE_MAP_READ, 0, 0, 2048);
if (m_FileMap)
{
m_Initialized = true;
}
else
{
LSLog(LOG_ERROR, L"Rainmeter", L"NowPlayingPlugin: Unable to view AIMP file mapping.");
}
}
else
{
LSLog(LOG_ERROR, L"Rainmeter", L"NowPlayingPlugin: AIMP - Unable to access AIMP file mapping.");
}
}
}
return m_Initialized;
}
/*
@ -123,29 +119,43 @@ bool CPlayerAIMP::CheckActive()
*/
void CPlayerAIMP::UpdateData()
{
static long oldFileSize;
static UINT oldTitleLen;
if (!CheckActive())
static long oldFileSize = 0;
static UINT oldTitleLen = 0;
if (!m_Initialized)
{
// Make sure AIMP is running
if (oldFileSize != 0)
if (oldTitleLen != 0)
{
oldFileSize = 0;
oldTitleLen = 0;
}
return;
if (!CheckWindow())
{
return;
}
}
// If initialized
m_State = (PLAYSTATE)SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_STATUS_GET, AIMP_STS_Player);
if (m_State == PLAYER_STOPPED)
{
if (oldFileSize != 0)
// Make sure AIMP is still active
if (!IsWindow(m_Window))
{
m_Initialized = false;
if (m_FileMap) UnmapViewOfFile(m_FileMap);
if (m_FileMapHandle) CloseHandle(m_FileMapHandle);
}
if (oldTitleLen != 0)
{
oldFileSize = 0;
oldTitleLen = 0;
ClearData();
}
// Don't continue if AIMP has quit or is stopped
return;
}
@ -154,7 +164,7 @@ void CPlayerAIMP::UpdateData()
AIMP2FileInfo* info = (AIMP2FileInfo*)m_FileMap;
if (info->cbSizeOf > 0 &&
info->nFileSize != oldFileSize || // FileSize and TitleLen are probably unique enough
info->nFileSize != oldFileSize || // Avoid reading the same file
info->nTitleLen != oldTitleLen)
{
oldFileSize = info->nFileSize;
@ -191,7 +201,12 @@ void CPlayerAIMP::UpdateData()
// Find cover if needed
if (m_HasCoverMeasure)
{
GetCover(m_Artist, m_Title, m_FilePath, m_CoverPath);
FindCover();
}
if (m_HasLyricsMeasure)
{
FindLyrics();
}
}
}

View File

@ -24,8 +24,9 @@
class CPlayerAIMP : public CPlayer
{
public:
CPlayerAIMP();
~CPlayerAIMP();
virtual ~CPlayerAIMP();
static CPlayer* Create();
virtual void UpdateData();
@ -40,14 +41,19 @@ public:
virtual void ClosePlayer();
virtual void OpenPlayer(std::wstring& path);
protected:
CPlayerAIMP();
private:
bool Initialize();
bool CheckActive();
bool CheckWindow();
LPVOID m_FileMap;
HANDLE m_FileMapHandle;
static CPlayer* c_Player;
HWND m_Window; // AIMP window
HWND m_WinampWindow; // AIMP Winamp API window
LPVOID m_FileMap;
HANDLE m_FileMapHandle;
};
#endif

View File

@ -20,7 +20,7 @@
#include "PlayerCAD.h"
#include "CAD/cad_sdk.h"
extern CPlayer* g_CAD;
CPlayer* CPlayerCAD::c_Player = NULL;
extern std::wstring g_SettingsFile;
// This player emulates the CD Art Display IPC interface, which is supported by
@ -47,10 +47,26 @@ CPlayerCAD::CPlayerCAD() : CPlayer(),
*/
CPlayerCAD::~CPlayerCAD()
{
g_CAD = NULL;
c_Player = NULL;
Uninitialize();
}
/*
** Create
**
** Creates a shared class object.
**
*/
CPlayer* CPlayerCAD::Create()
{
if (!c_Player)
{
c_Player = new CPlayerCAD();
}
return c_Player;
}
/*
** Initialize
**
@ -113,6 +129,7 @@ void CPlayerCAD::Initialize()
if (m_PlayerWindow)
{
m_Initialized = true;
SendMessage(m_PlayerWindow, WM_USER, (WPARAM)m_Window, IPC_SET_CALLBACK_HWND);
m_State = (PLAYSTATE)SendMessage(m_PlayerWindow, WM_USER, 0, IPC_GET_PLAYER_STATE);
@ -143,37 +160,37 @@ void CPlayerCAD::Uninitialize()
*/
LRESULT CALLBACK CPlayerCAD::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static CPlayerCAD* p;
static CPlayerCAD* player;
switch (msg)
{
case WM_CREATE:
// Get pointer to the CPlayerCAD class from the CreateWindow call
p = (CPlayerCAD*)((CREATESTRUCT*)lParam)->lpCreateParams;
player = (CPlayerCAD*)((CREATESTRUCT*)lParam)->lpCreateParams;
return 0;
case WM_DESTROY:
SendMessage(p->m_PlayerWindow, WM_USER, 0, IPC_SHUTDOWN_NOTIFICATION);
SendMessage(player->m_PlayerWindow, WM_USER, 0, IPC_SHUTDOWN_NOTIFICATION);
return 0;
case WM_USER:
switch (lParam)
{
case IPC_TRACK_CHANGED_NOTIFICATION:
SendMessage(p->m_PlayerWindow, WM_USER, 0, IPC_GET_CURRENT_TRACK);
SendMessage(player->m_PlayerWindow, WM_USER, 0, IPC_GET_CURRENT_TRACK);
break;
case IPC_PLAYER_STATE_CHANGED_NOTIFICATION:
p->m_State = (PLAYSTATE)wParam;
if (p->m_State == PLAYER_STOPPED)
player->m_State = (PLAYSTATE)wParam;
if (player->m_State == PLAYER_STOPPED)
{
p->ClearData();
player->ClearData();
}
break;
case IPC_SHUTDOWN_NOTIFICATION:
p->m_PlayerWindow = NULL;
p->ClearData();
player->m_Initialized = false;
player->ClearData();
break;
}
return 0;
@ -184,19 +201,19 @@ LRESULT CALLBACK CPlayerCAD::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
if (cds->dwData == IPC_CURRENT_TRACK_INFO)
{
// TODO: Sent on track update?
++p->m_TrackCount;
++player->m_TrackCount;
std::wstring data = (WCHAR*)cds->lpData;
std::wstring::size_type len = data.find_first_of(L'\t');
p->m_Title.assign(data, 0, len);
player->m_Title.assign(data, 0, len);
data.erase(0, ++len);
len = data.find_first_of(L'\t');
p->m_Artist.assign(data, 0, len);
player->m_Artist.assign(data, 0, len);
data.erase(0, ++len);
len = data.find_first_of(L'\t');
p->m_Album.assign(data, 0, len);
player->m_Album.assign(data, 0, len);
data.erase(0, ++len);
len = data.find_first_of(L'\t'); // Skip genre
@ -206,23 +223,28 @@ LRESULT CALLBACK CPlayerCAD::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
data.erase(0, ++len);
len = data.find_first_of(L'\t');
p->m_Duration = _wtoi(data.substr(0, len).c_str());
player->m_Duration = _wtoi(data.substr(0, len).c_str());
data.erase(0, ++len);
len = data.find_first_of(L'\t');
p->m_FilePath.assign(data, 0, len);
player->m_FilePath.assign(data, 0, len);
data.erase(0, ++len);
len = data.find_first_of(L'\t');
UINT rating = (_wtoi(data.substr(0, len).c_str()) + 1) / 2; // From 0 - 10 to 0 - 5
p->m_Rating = rating;
player->m_Rating = rating;
data.erase(0, ++len);
len = data.find_first_of(L'\t');
p->m_CoverPath.assign(data, 0, len);
player->m_CoverPath.assign(data, 0, len);
data.erase(0, ++len);
if (player->m_HasLyricsMeasure)
{
player->FindLyrics();
}
}
else if (cds->dwData == IPC_REGISTER_PLAYER && !p->m_PlayerWindow)
else if (cds->dwData == IPC_REGISTER_PLAYER && !player->m_Initialized)
{
std::wstring data = (WCHAR*)cds->lpData;
if (data[0] == L'1')
@ -238,7 +260,7 @@ LRESULT CALLBACK CPlayerCAD::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
data.erase(0, ++len);
len = data.find_first_of(L'\t');
p->m_PlayerPath.assign(data, 0, len);
player->m_PlayerPath.assign(data, 0, len);
data.erase(0, ++len);
LPCTSTR classSz = className.empty() ? NULL : className.c_str();
@ -247,17 +269,18 @@ LRESULT CALLBACK CPlayerCAD::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
WritePrivateProfileString(L"NowPlaying.dll", L"ClassName", classSz, file);
WritePrivateProfileString(L"NowPlaying.dll", L"WindowName", windowSz, file);
WritePrivateProfileString(L"NowPlaying.dll", L"PlayerPath", p->m_PlayerPath.c_str(), file);
WritePrivateProfileString(L"NowPlaying.dll", L"PlayerPath", player->m_PlayerPath.c_str(), file);
p->m_PlayerWindow = FindWindow(classSz, windowSz);
player->m_PlayerWindow = FindWindow(classSz, windowSz);
if (p->m_PlayerWindow)
if (player->m_PlayerWindow)
{
p->m_State = (PLAYSTATE)SendMessage(p->m_PlayerWindow, WM_USER, 0, IPC_GET_PLAYER_STATE);
player->m_Initialized = true;
player->m_State = (PLAYSTATE)SendMessage(player->m_PlayerWindow, WM_USER, 0, IPC_GET_PLAYER_STATE);
if (p->m_State != PLAYER_STOPPED)
if (player->m_State != PLAYER_STOPPED)
{
SendMessage(p->m_PlayerWindow, WM_USER, 0, IPC_GET_CURRENT_TRACK);
SendMessage(player->m_PlayerWindow, WM_USER, 0, IPC_GET_CURRENT_TRACK);
}
}
}
@ -392,7 +415,8 @@ void CPlayerCAD::SetVolume(int volume)
void CPlayerCAD::ClosePlayer()
{
SendMessage(m_PlayerWindow, WM_USER, 0, IPC_CLOSE_PLAYER);
m_PlayerWindow = NULL;
// TODO
m_Initialized = false;
ClearData();
}

View File

@ -24,8 +24,9 @@
class CPlayerCAD : public CPlayer
{
public:
CPlayerCAD();
~CPlayerCAD();
virtual ~CPlayerCAD();
static CPlayer* Create();
virtual void UpdateData();
@ -40,11 +41,16 @@ public:
virtual void ClosePlayer();
virtual void OpenPlayer(std::wstring& path);
protected:
CPlayerCAD();
private:
void Initialize();
void Uninitialize();
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
static CPlayer* c_Player;
HWND m_Window;
HWND m_PlayerWindow;
std::wstring m_PlayerPath;

View File

@ -19,7 +19,7 @@
#include "StdAfx.h"
#include "PlayerFoobar.h"
extern CPlayer* g_Foobar;
CPlayer* CPlayerFoobar::c_Player = NULL;
/*
** CPlayerFoobar
@ -42,10 +42,26 @@ CPlayerFoobar::CPlayerFoobar() : CPlayer(),
*/
CPlayerFoobar::~CPlayerFoobar()
{
g_Foobar = NULL;
c_Player = NULL;
Uninitialize();
}
/*
** Create
**
** Creates a shared class object.
**
*/
CPlayer* CPlayerFoobar::Create()
{
if (!c_Player)
{
c_Player = new CPlayerFoobar();
}
return c_Player;
}
/*
** Initialize
**
@ -89,6 +105,7 @@ void CPlayerFoobar::Initialize()
}
else
{
m_Initialized = true;
SendMessage(m_FooWindow, WM_USER, (WPARAM)m_Window, FOO_SETCALLBACK);
}
}
@ -164,7 +181,7 @@ LRESULT CALLBACK CPlayerFoobar::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPAR
break;
case FOO_PLAYERQUIT:
player->m_FooWindow = NULL;
player->m_Initialized = false;
player->ClearData();
break;
}
@ -221,7 +238,12 @@ LRESULT CALLBACK CPlayerFoobar::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPAR
if (player->m_HasCoverMeasure || player->m_InstanceCount == 0)
{
GetCover(player->m_Artist, player->m_Title, player->m_FilePath, player->m_CoverPath);
player->FindCover();
}
if (player->m_HasLyricsMeasure)
{
player->FindLyrics();
}
}
}
@ -339,7 +361,7 @@ void CPlayerFoobar::ClosePlayer()
*/
void CPlayerFoobar::OpenPlayer(std::wstring& path)
{
if (!m_FooWindow)
if (!m_Initialized)
{
if (path.empty())
{

View File

@ -24,8 +24,9 @@
class CPlayerFoobar : public CPlayer
{
public:
CPlayerFoobar();
~CPlayerFoobar();
virtual ~CPlayerFoobar();
static CPlayer* Create();
virtual void UpdateData();
@ -40,6 +41,9 @@ public:
virtual void ClosePlayer();
virtual void OpenPlayer(std::wstring& path);
protected:
CPlayerFoobar();
private:
enum FOOMESSAGE
{
@ -73,6 +77,8 @@ private:
void Uninitialize();
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
static CPlayer* c_Player;
HWND m_Window; // Our reciever window
HWND m_FooWindow; // Foobar receiver window
};

View File

@ -19,7 +19,7 @@
#include "StdAfx.h"
#include "PlayerITunes.h"
extern CPlayer* g_iTunes;
CPlayer* CPlayerITunes::c_Player = NULL;
/*
** CEventHandler
@ -129,11 +129,27 @@ CPlayerITunes::CPlayerITunes() : CPlayer(),
*/
CPlayerITunes::~CPlayerITunes()
{
g_iTunes = NULL;
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
**
@ -216,7 +232,6 @@ void CPlayerITunes::Uninitialize()
if (m_Initialized)
{
m_Initialized = false;
m_UserQuitPrompt = true;
if (m_iTunes)
{
m_iTunes->Release();
@ -304,6 +319,7 @@ void CPlayerITunes::OnTrackChange()
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;
@ -319,45 +335,54 @@ void CPlayerITunes::OnTrackChange()
++m_TrackCount;
m_FilePath = tmpStr;
if (m_HasCoverMeasure && !GetCachedCover(m_Artist, m_Title, m_CoverPath))
if (m_HasCoverMeasure)
{
// Art not in cache, check for embedded art
IITArtworkCollection* artworkCollection;
hr = track->get_Artwork(&artworkCollection);
if (SUCCEEDED(hr))
m_CoverPath = GetCacheFile();
if (!CCover::GetCached(m_CoverPath))
{
long count;
artworkCollection->get_Count(&count);
// Art not in cache, check for embedded art through iTunes interface
IITArtworkCollection* artworkCollection;
hr = track->get_Artwork(&artworkCollection);
if (count > 0)
if (SUCCEEDED(hr))
{
IITArtwork* artwork;
hr = artworkCollection->get_Item(1, &artwork);
long count;
artworkCollection->get_Count(&count);
if (SUCCEEDED(hr))
if (count > 0)
{
tmpStr = m_CoverPath.c_str();
hr = artwork->SaveArtworkToFile(tmpStr);
if (FAILED(hr))
{
m_CoverPath.clear();
}
IITArtwork* artwork;
hr = artworkCollection->get_Item(1, &artwork);
artwork->Release();
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();
}
}
}
artworkCollection->Release();
}
else
{
m_CoverPath.clear();
}
if (m_HasLyricsMeasure)
{
FindLyrics();
}
}
}

View File

@ -35,8 +35,9 @@
class CPlayerITunes : public CPlayer
{
public:
CPlayerITunes();
~CPlayerITunes();
virtual ~CPlayerITunes();
static CPlayer* Create();
virtual void UpdateData();
@ -51,6 +52,9 @@ public:
virtual void ClosePlayer();
virtual void OpenPlayer(std::wstring& path);
protected:
CPlayerITunes();
private:
class CEventHandler : public _IiTunesEvents
{
@ -83,6 +87,8 @@ private:
void OnVolumeChange(int volume);
bool CheckWindow();
static CPlayer* c_Player;
bool m_UserQuitPrompt;
IiTunes* m_iTunes;
CEventHandler* m_iTunesEvent;

View File

@ -19,7 +19,7 @@
#include "StdAfx.h"
#include "PlayerSpotify.h"
extern CPlayer* g_Spotify;
CPlayer* CPlayerSpotify::c_Player = NULL;
/*
** CPlayerSpotify
@ -40,7 +40,23 @@ CPlayerSpotify::CPlayerSpotify() : CPlayer(),
*/
CPlayerSpotify::~CPlayerSpotify()
{
g_Spotify = NULL;
c_Player = NULL;
}
/*
** Create
**
** Creates a shared class object.
**
*/
CPlayer* CPlayerSpotify::Create()
{
if (!c_Player)
{
c_Player = new CPlayerSpotify();
}
return c_Player;
}
/*
@ -98,6 +114,11 @@ void CPlayerSpotify::UpdateData()
m_Title = track;
m_Artist = artist;
++m_TrackCount;
if (m_HasLyricsMeasure)
{
FindLyrics();
}
}
return;
}

View File

@ -24,8 +24,9 @@
class CPlayerSpotify : public CPlayer
{
public:
CPlayerSpotify();
~CPlayerSpotify();
virtual ~CPlayerSpotify();
static CPlayer* Create();
virtual void Pause() { return Play(); }
virtual void Play();
@ -36,6 +37,9 @@ public:
virtual void OpenPlayer(std::wstring& path);
virtual void UpdateData();
protected:
CPlayerSpotify();
private:
enum SPOTIFYCOMMAND
{
@ -50,6 +54,8 @@ private:
bool CheckWindow();
static CPlayer* c_Player;
HWND m_Window;
};

View File

@ -19,7 +19,7 @@
#include "StdAfx.h"
#include "PlayerWLM.h"
extern CPlayer* g_WLM;
CPlayer* CPlayerWLM::c_Player = NULL;
// This player emulates the MSN/WLM Messenger 'Listening to' interface, which is
// supported by OpenPandora, Last.fm, Media Player Classic, TTPlayer, Zune, etc.
@ -54,6 +54,8 @@ CPlayerWLM::CPlayerWLM() : CPlayer(),
NULL,
hInstance,
this);
m_Initialized = true;
}
/*
@ -64,11 +66,27 @@ CPlayerWLM::CPlayerWLM() : CPlayer(),
*/
CPlayerWLM::~CPlayerWLM()
{
g_WLM = NULL;
c_Player = NULL;
DestroyWindow(m_Window);
UnregisterClass(L"MsnMsgrUIManager", GetModuleHandle(NULL));
}
/*
** Create
**
** Creates a shared class object.
**
*/
CPlayer* CPlayerWLM::Create()
{
if (!c_Player)
{
c_Player = new CPlayerWLM();
}
return c_Player;
}
LRESULT CALLBACK CPlayerWLM::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static CPlayerWLM* player;
@ -99,6 +117,7 @@ LRESULT CALLBACK CPlayerWLM::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM
if (playing)
{
++player->m_TrackCount;
player->m_State = PLAYER_PLAYING;
data.erase(0, 3); // Get rid of the status
@ -119,6 +138,11 @@ LRESULT CALLBACK CPlayerWLM::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM
len = data.find_first_of(L'\\');
player->m_Album = data.substr(0, len);
if (player->m_HasLyricsMeasure)
{
player->FindLyrics();
}
}
else
{

View File

@ -24,8 +24,9 @@
class CPlayerWLM : public CPlayer
{
public:
CPlayerWLM();
~CPlayerWLM();
virtual ~CPlayerWLM();
static CPlayer* Create();
virtual void UpdateData();
@ -35,10 +36,15 @@ public:
virtual void Next();
virtual void Previous();
protected:
CPlayerWLM();
private:
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void SendKeyInput(WORD key);
static CPlayer* c_Player;
HWND m_Window;
};

View File

@ -19,7 +19,7 @@
#include "StdAfx.h"
#include "PlayerWMP.h"
extern CPlayer* g_WMP;
CPlayer* CPlayerWMP::c_Player = NULL;
/*
** CRemoteHost
@ -170,11 +170,27 @@ CPlayerWMP::CPlayerWMP() : CPlayer(),
*/
CPlayerWMP::~CPlayerWMP()
{
g_WMP = NULL;
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
**
@ -450,9 +466,14 @@ void CPlayerWMP::UpdateData()
}
else
{
GetCover(m_Artist, m_Title, m_FilePath, m_CoverPath);
FindCover();
}
}
if (m_HasLyricsMeasure)
{
FindLyrics();
}
}
}
}

View File

@ -34,8 +34,9 @@
class CPlayerWMP : public CPlayer
{
public:
CPlayerWMP();
~CPlayerWMP();
virtual ~CPlayerWMP();
static CPlayer* Create();
virtual void UpdateData();
@ -50,6 +51,9 @@ public:
virtual void OpenPlayer(std::wstring& path);
virtual void ClosePlayer();
protected:
CPlayerWMP();
private:
class CRemoteHost :
public CComObjectRootEx<CComSingleThreadModel>,
@ -129,6 +133,8 @@ private:
void Initialize();
void Uninitialize();
static CPlayer* c_Player;
bool m_TrackChanged;
HWND m_Window;
CAxWindow* m_AxWindow;

View File

@ -21,7 +21,7 @@
#include "Winamp/wa_ipc.h"
#include "Winamp/wa_cmd.h"
extern CPlayer* g_Winamp;
CPlayer* CPlayerWinamp::c_Player = NULL;
// This player retrieves data through the Winamp IPC interface.
@ -32,9 +32,11 @@ extern CPlayer* g_Winamp;
**
*/
CPlayerWinamp::CPlayerWinamp(WINAMPTYPE type) : CPlayer(),
m_WinampType(type),
m_Window(),
m_UseUnicodeAPI(false),
m_Window()
m_WinampType(type),
m_WinampHandle(),
m_WinampAddress()
{
}
@ -46,10 +48,26 @@ CPlayerWinamp::CPlayerWinamp(WINAMPTYPE type) : CPlayer(),
*/
CPlayerWinamp::~CPlayerWinamp()
{
g_Winamp = NULL;
c_Player = NULL;
if (m_WinampHandle) CloseHandle(m_WinampHandle);
}
/*
** Create
**
** Creates a shared class object.
**
*/
CPlayer* CPlayerWinamp::Create(WINAMPTYPE type)
{
if (!c_Player)
{
c_Player = new CPlayerWinamp(type);
}
return c_Player;
}
/*
** CheckWindow
**
@ -204,63 +222,70 @@ void CPlayerWinamp::UpdateData()
}
}
// Find cover if needed
if (m_HasCoverMeasure &&
!GetCachedCover(m_Artist, m_Title, m_CoverPath) &&
!GetEmbeddedCover(fr, m_CoverPath))
if (m_HasLyricsMeasure)
{
std::wstring trackFolder = GetFileFolder(m_FilePath);
FindLyrics();
}
if (!m_Album.empty())
// Find cover if needed
if (m_HasCoverMeasure)
{
m_CoverPath = GetCacheFile();
if (!CCover::GetCached(m_CoverPath) &&
!CCover::GetEmbedded(fr, m_CoverPath))
{
// Winamp stores covers usually as %album%.jpg
std::wstring file = m_Album;
std::wstring::size_type end = file.length();
for (std::wstring::size_type pos = 0; pos < end; ++pos)
std::wstring trackFolder = CCover::GetFileFolder(m_FilePath);
if (!m_Album.empty())
{
// Replace reserved chars according to Winamp specs
switch (file[pos])
// Winamp stores covers usually as %album%.jpg
std::wstring file = m_Album;
std::wstring::size_type end = file.length();
for (std::wstring::size_type pos = 0; pos < end; ++pos)
{
case L'?':
case L'*':
case L'|':
file[pos] = L'_';
break;
// Replace reserved chars according to Winamp specs
switch (file[pos])
{
case L'?':
case L'*':
case L'|':
file[pos] = L'_';
break;
case L'/':
case L'\\':
case L':':
file[pos] = L'-';
break;
case L'/':
case L'\\':
case L':':
file[pos] = L'-';
break;
case L'\"':
file[pos] = L'\'';
break;
case L'\"':
file[pos] = L'\'';
break;
case L'<':
file[pos] = L'(';
break;
case L'<':
file[pos] = L'(';
break;
case L'>':
file[pos] = L')';
break;
case L'>':
file[pos] = L')';
break;
}
}
if (CCover::GetLocal(file, trackFolder, m_CoverPath))
{
// %album% art file found
return;
}
}
if (GetLocalCover(file, trackFolder, m_CoverPath))
if (!CCover::GetLocal(L"cover", trackFolder, m_CoverPath) &&
!CCover::GetLocal(L"folder", trackFolder, m_CoverPath))
{
// %album% art file found
return;
// Nothing found
m_CoverPath.clear();
}
}
if (!GetLocalCover(L"cover", trackFolder, m_CoverPath) &&
!GetLocalCover(L"folder", trackFolder, m_CoverPath))
{
// Nothing found
m_CoverPath.clear();
}
}
}
}

View File

@ -30,8 +30,9 @@ enum WINAMPTYPE
class CPlayerWinamp : public CPlayer
{
public:
CPlayerWinamp(WINAMPTYPE type);
~CPlayerWinamp();
virtual ~CPlayerWinamp();
static CPlayer* Create(WINAMPTYPE type);
virtual void UpdateData();
@ -46,12 +47,17 @@ public:
virtual void ClosePlayer();
virtual void OpenPlayer(std::wstring& path);
protected:
CPlayerWinamp(WINAMPTYPE type);
private:
bool CheckWindow();
static CPlayer* c_Player;
HWND m_Window; // Winamp window
bool m_UseUnicodeAPI;
WINAMPTYPE m_WinampType;
HWND m_Window; // Winamp window
HANDLE m_WinampHandle; // Handle to Winamp process
LPCVOID m_WinampAddress;
};

View File

@ -122,6 +122,7 @@
<ProgramDatabaseFile>.\x32/Debug/NowPlaying.pdb</ProgramDatabaseFile>
<ImportLibrary>.\x32/Debug/NowPlaying.lib</ImportLibrary>
<TargetMachine>MachineX86</TargetMachine>
<AdditionalDependencies>WinInet.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -162,6 +163,7 @@
<ProgramDatabaseFile>.\x64/Debug/NowPlaying.pdb</ProgramDatabaseFile>
<ImportLibrary>.\x64/Debug/NowPlaying.lib</ImportLibrary>
<TargetMachine>MachineX64</TargetMachine>
<AdditionalDependencies>WinInet.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@ -198,7 +200,7 @@
<Culture>0x0409</Culture>
</ResourceCompile>
<Link>
<AdditionalDependencies>Rainmeter.lib;Wininet.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Rainmeter.lib;WinInet.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>../../TestBench/x32/Release/Plugins/NowPlaying.dll</OutputFile>
<SuppressStartupBanner>true</SuppressStartupBanner>
<AdditionalLibraryDirectories>..\..\Library\x32\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
@ -243,7 +245,7 @@
</ResourceCompile>
<Link>
<AdditionalOptions>/LTCG %(AdditionalOptions)</AdditionalOptions>
<AdditionalDependencies>Rainmeter.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Rainmeter.lib;WinInet.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>../../TestBench/x64/Release/Plugins/NowPlaying.dll</OutputFile>
<SuppressStartupBanner>true</SuppressStartupBanner>
<AdditionalLibraryDirectories>..\..\Library\x64\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
@ -256,6 +258,8 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="Cover.cpp" />
<ClCompile Include="Internet.cpp" />
<ClCompile Include="Lyrics.cpp" />
<ClCompile Include="NowPlaying.cpp" />
<ClCompile Include="Player.cpp" />
<ClCompile Include="PlayerAIMP.cpp" />
@ -344,6 +348,8 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="Cover.h" />
<ClInclude Include="Internet.h" />
<ClInclude Include="Lyrics.h" />
<ClInclude Include="NowPlaying.h" />
<ClInclude Include="Player.h" />
<ClInclude Include="PlayerAIMP.h" />

View File

@ -267,6 +267,12 @@
<ClCompile Include="Cover.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Lyrics.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Internet.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="SDKs\AIMP\aimp2_sdk.h">
@ -326,6 +332,12 @@
<ClInclude Include="Cover.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Lyrics.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Internet.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="PluginNowPlaying.rc">

View File

@ -20,7 +20,8 @@
#define __STDAFX_H__
// WinAPI
#include <windows.h>
#include <Windows.h>
#include <WinInet.h>
// STL
#include <string>
@ -32,7 +33,4 @@
// Rainmeter's exported functions
#include "../../Library/Export.h"
// TagLib
#include "fileref.h"
#endif