From d633f4b586e971fb6d69dba659af77ec3c148c46 Mon Sep 17 00:00:00 2001 From: Birunthan Mohanathas Date: Sun, 19 Jun 2011 14:58:48 +0000 Subject: [PATCH] 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 --- Plugins/PluginNowPlaying/Cover.cpp | 67 +---- Plugins/PluginNowPlaying/Cover.h | 9 +- Plugins/PluginNowPlaying/Internet.cpp | 281 ++++++++++++++++++ Plugins/PluginNowPlaying/Internet.h | 37 +++ Plugins/PluginNowPlaying/Lyrics.cpp | 204 +++++++++++++ Plugins/PluginNowPlaying/Lyrics.h | 33 ++ Plugins/PluginNowPlaying/NowPlaying.cpp | 77 ++--- Plugins/PluginNowPlaying/NowPlaying.h | 3 +- Plugins/PluginNowPlaying/Player.cpp | 134 ++++++++- Plugins/PluginNowPlaying/Player.h | 16 +- Plugins/PluginNowPlaying/PlayerAIMP.cpp | 143 +++++---- Plugins/PluginNowPlaying/PlayerAIMP.h | 16 +- Plugins/PluginNowPlaying/PlayerCAD.cpp | 80 +++-- Plugins/PluginNowPlaying/PlayerCAD.h | 10 +- Plugins/PluginNowPlaying/PlayerFoobar.cpp | 32 +- Plugins/PluginNowPlaying/PlayerFoobar.h | 10 +- Plugins/PluginNowPlaying/PlayerITunes.cpp | 81 +++-- Plugins/PluginNowPlaying/PlayerITunes.h | 10 +- Plugins/PluginNowPlaying/PlayerSpotify.cpp | 25 +- Plugins/PluginNowPlaying/PlayerSpotify.h | 10 +- Plugins/PluginNowPlaying/PlayerWLM.cpp | 28 +- Plugins/PluginNowPlaying/PlayerWLM.h | 10 +- Plugins/PluginNowPlaying/PlayerWMP.cpp | 27 +- Plugins/PluginNowPlaying/PlayerWMP.h | 10 +- Plugins/PluginNowPlaying/PlayerWinamp.cpp | 117 +++++--- Plugins/PluginNowPlaying/PlayerWinamp.h | 12 +- .../PluginNowPlaying/PluginNowPlaying.vcxproj | 10 +- .../PluginNowPlaying.vcxproj.filters | 12 + Plugins/PluginNowPlaying/StdAfx.h | 6 +- 29 files changed, 1181 insertions(+), 329 deletions(-) create mode 100644 Plugins/PluginNowPlaying/Internet.cpp create mode 100644 Plugins/PluginNowPlaying/Internet.h create mode 100644 Plugins/PluginNowPlaying/Lyrics.cpp create mode 100644 Plugins/PluginNowPlaying/Lyrics.h diff --git a/Plugins/PluginNowPlaying/Cover.cpp b/Plugins/PluginNowPlaying/Cover.cpp index 46cea44c..1d692855 100644 --- a/Plugins/PluginNowPlaying/Cover.cpp +++ b/Plugins/PluginNowPlaying/Cover.cpp @@ -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; diff --git a/Plugins/PluginNowPlaying/Cover.h b/Plugins/PluginNowPlaying/Cover.h index 7f5da634..554d8a4c 100644 --- a/Plugins/PluginNowPlaying/Cover.h +++ b/Plugins/PluginNowPlaying/Cover.h @@ -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: diff --git a/Plugins/PluginNowPlaying/Internet.cpp b/Plugins/PluginNowPlaying/Internet.cpp new file mode 100644 index 00000000..765f3e9d --- /dev/null +++ b/Plugins/PluginNowPlaying/Internet.cpp @@ -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; +} diff --git a/Plugins/PluginNowPlaying/Internet.h b/Plugins/PluginNowPlaying/Internet.h new file mode 100644 index 00000000..384d81e4 --- /dev/null +++ b/Plugins/PluginNowPlaying/Internet.h @@ -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 \ No newline at end of file diff --git a/Plugins/PluginNowPlaying/Lyrics.cpp b/Plugins/PluginNowPlaying/Lyrics.cpp new file mode 100644 index 00000000..a9f654e4 --- /dev/null +++ b/Plugins/PluginNowPlaying/Lyrics.cpp @@ -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""); + if (pos != std::wstring::npos) + { + // Skip unavailable lyrics + return ret; + } + + while ((pos = data.find(L"
"), 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"

", pos); + if (pos != std::wstring::npos) + { + pos += 3; + data.erase(0, pos); + + pos = data.find(L"

"); + data.resize(pos); + + CInternet::DecodeReferences(data); + + while ((pos = data.find(L"
"), pos) != std::wstring::npos) + { + data.erase(pos, 5); + } + + ret = true; + } + } + + return ret; +} diff --git a/Plugins/PluginNowPlaying/Lyrics.h b/Plugins/PluginNowPlaying/Lyrics.h new file mode 100644 index 00000000..d76ca656 --- /dev/null +++ b/Plugins/PluginNowPlaying/Lyrics.h @@ -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 \ No newline at end of file diff --git a/Plugins/PluginNowPlaying/NowPlaying.cpp b/Plugins/PluginNowPlaying/NowPlaying.cpp index ccc79a4b..80be0079 100644 --- a/Plugins/PluginNowPlaying/NowPlaying.cpp +++ b/Plugins/PluginNowPlaying/NowPlaying.cpp @@ -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 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(); diff --git a/Plugins/PluginNowPlaying/NowPlaying.h b/Plugins/PluginNowPlaying/NowPlaying.h index e0eff26f..b3576504 100644 --- a/Plugins/PluginNowPlaying/NowPlaying.h +++ b/Plugins/PluginNowPlaying/NowPlaying.h @@ -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 diff --git a/Plugins/PluginNowPlaying/Player.cpp b/Plugins/PluginNowPlaying/Player.cpp index 22c088c9..01db1acf 100644 --- a/Plugins/PluginNowPlaying/Player.cpp +++ b/Plugins/PluginNowPlaying/Player.cpp @@ -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(); } diff --git a/Plugins/PluginNowPlaying/Player.h b/Plugins/PluginNowPlaying/Player.h index bf3e95ff..a34c74aa 100644 --- a/Plugins/PluginNowPlaying/Player.h +++ b/Plugins/PluginNowPlaying/Player.h @@ -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 \ No newline at end of file diff --git a/Plugins/PluginNowPlaying/PlayerAIMP.cpp b/Plugins/PluginNowPlaying/PlayerAIMP.cpp index 3cd148f9..199c6b17 100644 --- a/Plugins/PluginNowPlaying/PlayerAIMP.cpp +++ b/Plugins/PluginNowPlaying/PlayerAIMP.cpp @@ -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(); } } } diff --git a/Plugins/PluginNowPlaying/PlayerAIMP.h b/Plugins/PluginNowPlaying/PlayerAIMP.h index 84ad6c0c..f59274e1 100644 --- a/Plugins/PluginNowPlaying/PlayerAIMP.h +++ b/Plugins/PluginNowPlaying/PlayerAIMP.h @@ -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 diff --git a/Plugins/PluginNowPlaying/PlayerCAD.cpp b/Plugins/PluginNowPlaying/PlayerCAD.cpp index 290716b9..65bd5316 100644 --- a/Plugins/PluginNowPlaying/PlayerCAD.cpp +++ b/Plugins/PluginNowPlaying/PlayerCAD.cpp @@ -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(); } diff --git a/Plugins/PluginNowPlaying/PlayerCAD.h b/Plugins/PluginNowPlaying/PlayerCAD.h index 202ba198..0ad6ad3e 100644 --- a/Plugins/PluginNowPlaying/PlayerCAD.h +++ b/Plugins/PluginNowPlaying/PlayerCAD.h @@ -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; diff --git a/Plugins/PluginNowPlaying/PlayerFoobar.cpp b/Plugins/PluginNowPlaying/PlayerFoobar.cpp index 91969a87..012d24a4 100644 --- a/Plugins/PluginNowPlaying/PlayerFoobar.cpp +++ b/Plugins/PluginNowPlaying/PlayerFoobar.cpp @@ -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()) { diff --git a/Plugins/PluginNowPlaying/PlayerFoobar.h b/Plugins/PluginNowPlaying/PlayerFoobar.h index 5abb717d..bd740479 100644 --- a/Plugins/PluginNowPlaying/PlayerFoobar.h +++ b/Plugins/PluginNowPlaying/PlayerFoobar.h @@ -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 }; diff --git a/Plugins/PluginNowPlaying/PlayerITunes.cpp b/Plugins/PluginNowPlaying/PlayerITunes.cpp index 24a31043..52690f79 100644 --- a/Plugins/PluginNowPlaying/PlayerITunes.cpp +++ b/Plugins/PluginNowPlaying/PlayerITunes.cpp @@ -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(); } } } diff --git a/Plugins/PluginNowPlaying/PlayerITunes.h b/Plugins/PluginNowPlaying/PlayerITunes.h index 62259342..e3f69cb1 100644 --- a/Plugins/PluginNowPlaying/PlayerITunes.h +++ b/Plugins/PluginNowPlaying/PlayerITunes.h @@ -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; diff --git a/Plugins/PluginNowPlaying/PlayerSpotify.cpp b/Plugins/PluginNowPlaying/PlayerSpotify.cpp index 1acabc44..c33763c3 100644 --- a/Plugins/PluginNowPlaying/PlayerSpotify.cpp +++ b/Plugins/PluginNowPlaying/PlayerSpotify.cpp @@ -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; } diff --git a/Plugins/PluginNowPlaying/PlayerSpotify.h b/Plugins/PluginNowPlaying/PlayerSpotify.h index ee03125d..ded97a9a 100644 --- a/Plugins/PluginNowPlaying/PlayerSpotify.h +++ b/Plugins/PluginNowPlaying/PlayerSpotify.h @@ -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; }; diff --git a/Plugins/PluginNowPlaying/PlayerWLM.cpp b/Plugins/PluginNowPlaying/PlayerWLM.cpp index edcc272c..3a870421 100644 --- a/Plugins/PluginNowPlaying/PlayerWLM.cpp +++ b/Plugins/PluginNowPlaying/PlayerWLM.cpp @@ -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 { diff --git a/Plugins/PluginNowPlaying/PlayerWLM.h b/Plugins/PluginNowPlaying/PlayerWLM.h index 5f797513..b27bfa9c 100644 --- a/Plugins/PluginNowPlaying/PlayerWLM.h +++ b/Plugins/PluginNowPlaying/PlayerWLM.h @@ -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; }; diff --git a/Plugins/PluginNowPlaying/PlayerWMP.cpp b/Plugins/PluginNowPlaying/PlayerWMP.cpp index 9e22643d..f3fda6b3 100644 --- a/Plugins/PluginNowPlaying/PlayerWMP.cpp +++ b/Plugins/PluginNowPlaying/PlayerWMP.cpp @@ -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(); + } } } } diff --git a/Plugins/PluginNowPlaying/PlayerWMP.h b/Plugins/PluginNowPlaying/PlayerWMP.h index e1c1d565..70973840 100644 --- a/Plugins/PluginNowPlaying/PlayerWMP.h +++ b/Plugins/PluginNowPlaying/PlayerWMP.h @@ -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, @@ -129,6 +133,8 @@ private: void Initialize(); void Uninitialize(); + static CPlayer* c_Player; + bool m_TrackChanged; HWND m_Window; CAxWindow* m_AxWindow; diff --git a/Plugins/PluginNowPlaying/PlayerWinamp.cpp b/Plugins/PluginNowPlaying/PlayerWinamp.cpp index 2d85ff07..28b8a143 100644 --- a/Plugins/PluginNowPlaying/PlayerWinamp.cpp +++ b/Plugins/PluginNowPlaying/PlayerWinamp.cpp @@ -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(); - } - } } } diff --git a/Plugins/PluginNowPlaying/PlayerWinamp.h b/Plugins/PluginNowPlaying/PlayerWinamp.h index 003661d7..b75e6069 100644 --- a/Plugins/PluginNowPlaying/PlayerWinamp.h +++ b/Plugins/PluginNowPlaying/PlayerWinamp.h @@ -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; }; diff --git a/Plugins/PluginNowPlaying/PluginNowPlaying.vcxproj b/Plugins/PluginNowPlaying/PluginNowPlaying.vcxproj index 06bfe18f..26b1d03c 100644 --- a/Plugins/PluginNowPlaying/PluginNowPlaying.vcxproj +++ b/Plugins/PluginNowPlaying/PluginNowPlaying.vcxproj @@ -122,6 +122,7 @@ .\x32/Debug/NowPlaying.pdb .\x32/Debug/NowPlaying.lib MachineX86 + WinInet.lib;%(AdditionalDependencies) @@ -162,6 +163,7 @@ .\x64/Debug/NowPlaying.pdb .\x64/Debug/NowPlaying.lib MachineX64 + WinInet.lib;%(AdditionalDependencies) @@ -198,7 +200,7 @@ 0x0409 - Rainmeter.lib;Wininet.lib;%(AdditionalDependencies) + Rainmeter.lib;WinInet.lib;%(AdditionalDependencies) ../../TestBench/x32/Release/Plugins/NowPlaying.dll true ..\..\Library\x32\Release;%(AdditionalLibraryDirectories) @@ -243,7 +245,7 @@ /LTCG %(AdditionalOptions) - Rainmeter.lib;%(AdditionalDependencies) + Rainmeter.lib;WinInet.lib;%(AdditionalDependencies) ../../TestBench/x64/Release/Plugins/NowPlaying.dll true ..\..\Library\x64\Release;%(AdditionalLibraryDirectories) @@ -256,6 +258,8 @@ + + @@ -344,6 +348,8 @@ + + diff --git a/Plugins/PluginNowPlaying/PluginNowPlaying.vcxproj.filters b/Plugins/PluginNowPlaying/PluginNowPlaying.vcxproj.filters index aa1ee991..8d176448 100644 --- a/Plugins/PluginNowPlaying/PluginNowPlaying.vcxproj.filters +++ b/Plugins/PluginNowPlaying/PluginNowPlaying.vcxproj.filters @@ -267,6 +267,12 @@ Source Files + + Source Files + + + Source Files + @@ -326,6 +332,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/Plugins/PluginNowPlaying/StdAfx.h b/Plugins/PluginNowPlaying/StdAfx.h index 7220d915..44785b53 100644 --- a/Plugins/PluginNowPlaying/StdAfx.h +++ b/Plugins/PluginNowPlaying/StdAfx.h @@ -20,7 +20,8 @@ #define __STDAFX_H__ // WinAPI -#include +#include +#include // STL #include @@ -32,7 +33,4 @@ // Rainmeter's exported functions #include "../../Library/Export.h" -// TagLib -#include "fileref.h" - #endif