2011-05-21 18:17:37 +00:00
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Common functions for the players
|
|
|
|
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "Player.h"
|
|
|
|
|
|
|
|
extern std::wstring g_CachePath;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** CPlayer
|
|
|
|
**
|
|
|
|
** Constructor.
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
CPlayer::CPlayer() :
|
|
|
|
m_InstanceCount(),
|
|
|
|
m_State(),
|
|
|
|
m_Duration(),
|
|
|
|
m_Position(),
|
|
|
|
m_Rating(),
|
|
|
|
m_Volume(),
|
|
|
|
m_TrackChanged(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** ~CPlayer
|
|
|
|
**
|
|
|
|
** Destructor.
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
CPlayer::~CPlayer()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** ExecuteTrackChangeAction
|
|
|
|
**
|
|
|
|
** Called from player implementation on track change.
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
void CPlayer::ExecuteTrackChangeAction()
|
|
|
|
{
|
|
|
|
if (!m_TrackChangeAction.empty())
|
|
|
|
{
|
|
|
|
HWND wnd = FindWindow(L"RainmeterMeterWindow", NULL);
|
|
|
|
if (wnd != NULL)
|
|
|
|
{
|
|
|
|
COPYDATASTRUCT cds;
|
|
|
|
cds.dwData = 1;
|
|
|
|
cds.cbData = (DWORD)(m_TrackChangeAction.size() + 1) * sizeof(WCHAR);
|
|
|
|
cds.lpData = (void*)m_TrackChangeAction.c_str();
|
|
|
|
|
|
|
|
// Send the bang to the Rainmeter window
|
|
|
|
SendMessage(wnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
** ClearInfo
|
|
|
|
**
|
|
|
|
** Clear track information.
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
void CPlayer::ClearInfo()
|
|
|
|
{
|
|
|
|
m_Duration = 0;
|
|
|
|
m_Position = 0;
|
|
|
|
m_Rating = 0;
|
|
|
|
m_State = PLAYER_STOPPED;
|
|
|
|
m_Artist.clear();
|
|
|
|
m_Album.clear();
|
|
|
|
m_Title.clear();
|
2011-05-22 08:06:43 +00:00
|
|
|
m_FilePath.clear();
|
2011-05-21 18:17:37 +00:00
|
|
|
m_CoverPath.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-05-22 11:05:23 +00:00
|
|
|
** GetCachedArt
|
2011-05-21 18:17:37 +00:00
|
|
|
**
|
2011-05-22 11:05:23 +00:00
|
|
|
** Checks if cover art is in cache.
|
2011-05-21 18:17:37 +00:00
|
|
|
**
|
|
|
|
*/
|
2011-05-22 11:05:23 +00:00
|
|
|
bool CPlayer::GetCachedArt()
|
2011-05-21 18:17:37 +00:00
|
|
|
{
|
2011-05-22 11:05:23 +00:00
|
|
|
m_CoverPath = g_CachePath;
|
2011-05-21 18:17:37 +00:00
|
|
|
if (m_Artist.empty() || m_Title.empty())
|
|
|
|
{
|
2011-05-22 11:05:23 +00:00
|
|
|
m_CoverPath += L"temp.art";
|
2011-05-21 18:17:37 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Otherwise, save it as "Artist - Title.art"
|
|
|
|
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'_';
|
2011-05-22 11:05:23 +00:00
|
|
|
|
|
|
|
m_CoverPath += name;
|
|
|
|
m_CoverPath += L".art";
|
|
|
|
if (_waccess(m_CoverPath.c_str(), 0) == 0)
|
|
|
|
{
|
|
|
|
// Art found in cache
|
|
|
|
return true;
|
|
|
|
}
|
2011-05-21 18:17:37 +00:00
|
|
|
}
|
|
|
|
|
2011-05-22 11:05:23 +00:00
|
|
|
return false;
|
2011-05-21 18:17:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-05-22 11:05:23 +00:00
|
|
|
** GetLocalArt
|
2011-05-21 18:17:37 +00:00
|
|
|
**
|
|
|
|
** Attemps to find local cover art in various formats.
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
bool CPlayer::GetLocalArt(std::wstring& folder, std::wstring filename)
|
|
|
|
{
|
|
|
|
std::wstring testPath = folder;
|
|
|
|
testPath += filename;
|
|
|
|
testPath += L".";
|
|
|
|
std::wstring::size_type origLen = testPath.length();
|
|
|
|
|
|
|
|
const int extCount = 4;
|
|
|
|
LPCTSTR extName[extCount] = { L"jpg", L"jpeg", L"png", L"bmp" };
|
|
|
|
|
|
|
|
for (int i = 0; i < extCount; ++i)
|
|
|
|
{
|
|
|
|
testPath += extName[i];
|
|
|
|
if (_waccess(testPath.c_str(), 0) == 0)
|
|
|
|
{
|
|
|
|
m_CoverPath = testPath;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Get rid of the added extension
|
|
|
|
testPath.resize(origLen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-05-22 11:05:23 +00:00
|
|
|
** GetEmbeddedArt
|
2011-05-21 18:17:37 +00:00
|
|
|
**
|
|
|
|
** Attempts to extract cover art from audio files.
|
|
|
|
**
|
|
|
|
*/
|
2011-05-22 11:05:23 +00:00
|
|
|
bool CPlayer::GetEmbeddedArt(const TagLib::FileRef& fr)
|
2011-05-21 18:17:37 +00:00
|
|
|
{
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
if (TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(fr.file()))
|
|
|
|
{
|
|
|
|
if (file->ID3v2Tag())
|
|
|
|
{
|
2011-05-22 11:05:23 +00:00
|
|
|
found = GetArtID3(file->ID3v2Tag());
|
2011-05-21 18:17:37 +00:00
|
|
|
}
|
|
|
|
if (!found && file->APETag())
|
|
|
|
{
|
2011-05-22 11:05:23 +00:00
|
|
|
found = GetArtAPE(file->APETag());
|
2011-05-21 18:17:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (TagLib::MP4::File* file = dynamic_cast<TagLib::MP4::File*>(fr.file()))
|
|
|
|
{
|
|
|
|
if (file->tag())
|
|
|
|
{
|
2011-05-22 11:05:23 +00:00
|
|
|
found = GetArtMP4(file);
|
2011-05-21 18:17:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (TagLib::FLAC::File* file = dynamic_cast<TagLib::FLAC::File*>(fr.file()))
|
|
|
|
{
|
2011-05-22 11:05:23 +00:00
|
|
|
found = GetArtFLAC(file);
|
2011-05-21 18:17:37 +00:00
|
|
|
|
|
|
|
if (!found && file->ID3v2Tag())
|
|
|
|
{
|
2011-05-22 11:05:23 +00:00
|
|
|
found = GetArtID3(file->ID3v2Tag());
|
2011-05-21 18:17:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (TagLib::ASF::File* file = dynamic_cast<TagLib::ASF::File*>(fr.file()))
|
|
|
|
{
|
2011-05-22 11:05:23 +00:00
|
|
|
found = GetArtASF(file);
|
2011-05-21 18:17:37 +00:00
|
|
|
}
|
|
|
|
else if (TagLib::APE::File* file = dynamic_cast<TagLib::APE::File*>(fr.file()))
|
|
|
|
{
|
|
|
|
if (file->APETag())
|
|
|
|
{
|
2011-05-22 11:05:23 +00:00
|
|
|
found = GetArtAPE(file->APETag());
|
2011-05-21 18:17:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (TagLib::MPC::File* file = dynamic_cast<TagLib::MPC::File*>(fr.file()))
|
|
|
|
{
|
|
|
|
if (file->APETag())
|
|
|
|
{
|
2011-05-22 11:05:23 +00:00
|
|
|
found = GetArtAPE(file->APETag());
|
2011-05-21 18:17:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (TagLib::WavPack::File* file = dynamic_cast<TagLib::WavPack::File*>(fr.file()))
|
|
|
|
{
|
|
|
|
if (file->APETag())
|
|
|
|
{
|
2011-05-22 11:05:23 +00:00
|
|
|
found = GetArtAPE(file->APETag());
|
2011-05-21 18:17:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** GetArtAPE
|
|
|
|
**
|
|
|
|
** Extracts cover art embedded in APE tags.
|
|
|
|
**
|
|
|
|
*/
|
2011-05-22 11:05:23 +00:00
|
|
|
bool CPlayer::GetArtAPE(TagLib::APE::Tag* tag)
|
2011-05-21 18:17:37 +00:00
|
|
|
{
|
|
|
|
bool ret = false;
|
|
|
|
const TagLib::APE::ItemListMap& listMap = tag->itemListMap();
|
|
|
|
|
|
|
|
if (listMap.contains("COVER ART (FRONT)"))
|
|
|
|
{
|
|
|
|
const TagLib::ByteVector nullStringTerminator(1, 0);
|
|
|
|
|
|
|
|
TagLib::ByteVector item = listMap["COVER ART (FRONT)"].value();
|
|
|
|
int pos = item.find(nullStringTerminator); // Skip the filename
|
|
|
|
|
|
|
|
if (++pos > 0)
|
|
|
|
{
|
|
|
|
const TagLib::ByteVector& pic = item.mid(pos);
|
|
|
|
|
2011-05-22 11:05:23 +00:00
|
|
|
FILE* f = _wfopen(m_CoverPath.c_str(), L"wb");
|
2011-05-21 18:17:37 +00:00
|
|
|
if (f)
|
|
|
|
{
|
|
|
|
ret = (fwrite(pic.data(), 1, pic.size(), f) == pic.size());
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-06-09 09:47:55 +00:00
|
|
|
** GetArtID3
|
2011-05-21 18:17:37 +00:00
|
|
|
**
|
|
|
|
** Extracts cover art embedded in ID3v2 tags.
|
|
|
|
**
|
|
|
|
*/
|
2011-05-22 11:05:23 +00:00
|
|
|
bool CPlayer::GetArtID3(TagLib::ID3v2::Tag* tag)
|
2011-05-21 18:17:37 +00:00
|
|
|
{
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
const TagLib::ID3v2::FrameList& frameList = tag->frameList("APIC");
|
|
|
|
if (!frameList.isEmpty())
|
|
|
|
{
|
|
|
|
// Grab the first image
|
|
|
|
TagLib::ID3v2::AttachedPictureFrame* frame = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(frameList.front());
|
|
|
|
TagLib::uint size = frame->picture().size();
|
|
|
|
|
|
|
|
if (size > 0)
|
|
|
|
{
|
2011-05-22 11:05:23 +00:00
|
|
|
FILE* f = _wfopen(m_CoverPath.c_str(), L"wb");
|
2011-05-21 18:17:37 +00:00
|
|
|
if (f)
|
|
|
|
{
|
|
|
|
ret = (fwrite(frame->picture().data(), 1, size, f) == size);
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** GetArtASF
|
|
|
|
**
|
|
|
|
** Extracts cover art embedded in ASF/WMA files.
|
|
|
|
**
|
|
|
|
*/
|
2011-05-22 11:05:23 +00:00
|
|
|
bool CPlayer::GetArtASF(TagLib::ASF::File* file)
|
2011-05-21 18:17:37 +00:00
|
|
|
{
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
const TagLib::ASF::AttributeListMap& attrListMap = file->tag()->attributeListMap();
|
|
|
|
|
|
|
|
if (attrListMap.contains("WM/Picture"))
|
|
|
|
{
|
|
|
|
const TagLib::ASF::AttributeList& attrList = attrListMap["WM/Picture"];
|
|
|
|
|
|
|
|
if (!attrList.isEmpty())
|
|
|
|
{
|
|
|
|
// Let's grab the first cover. TODO: Check/loop for correct type
|
|
|
|
TagLib::ASF::Picture wmpic = attrList[0].toPicture();
|
|
|
|
|
|
|
|
if (wmpic.isValid())
|
|
|
|
{
|
2011-05-22 11:05:23 +00:00
|
|
|
FILE* f = _wfopen(m_CoverPath.c_str(), L"wb");
|
2011-05-21 18:17:37 +00:00
|
|
|
if (f)
|
|
|
|
{
|
|
|
|
ret = (fwrite(wmpic.picture().data(), 1, wmpic.picture().size(), f) == wmpic.picture().size());
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** GetArtFLAC
|
|
|
|
**
|
|
|
|
** Extracts cover art embedded in FLAC files.
|
|
|
|
**
|
|
|
|
*/
|
2011-05-22 11:05:23 +00:00
|
|
|
bool CPlayer::GetArtFLAC(TagLib::FLAC::File* file)
|
2011-05-21 18:17:37 +00:00
|
|
|
{
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
const TagLib::List<TagLib::FLAC::Picture*>& picList = file->pictureList();
|
|
|
|
if (!picList.isEmpty())
|
|
|
|
{
|
|
|
|
// Let's grab the first image
|
|
|
|
TagLib::FLAC::Picture* pic = picList[0];
|
|
|
|
|
2011-05-22 11:05:23 +00:00
|
|
|
FILE* f = _wfopen(m_CoverPath.c_str(), L"wb");
|
2011-05-21 18:17:37 +00:00
|
|
|
if (f)
|
|
|
|
{
|
|
|
|
ret = (fwrite(pic->data().data(), 1, pic->data().size(), f) == pic->data().size());
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** GetArtMP4
|
|
|
|
**
|
|
|
|
** Extracts cover art embedded in MP4-like files.
|
|
|
|
**
|
|
|
|
*/
|
2011-05-22 11:05:23 +00:00
|
|
|
bool CPlayer::GetArtMP4(TagLib::MP4::File* file)
|
2011-05-21 18:17:37 +00:00
|
|
|
{
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
TagLib::MP4::Tag* tag = file->tag();
|
|
|
|
if (tag->itemListMap().contains("covr"))
|
|
|
|
{
|
|
|
|
TagLib::MP4::CoverArtList coverList = tag->itemListMap()["covr"].toCoverArtList();
|
|
|
|
TagLib::uint size = coverList[0].data().size();
|
|
|
|
|
|
|
|
if (size > 0)
|
|
|
|
{
|
2011-05-22 11:05:23 +00:00
|
|
|
FILE* f = _wfopen(m_CoverPath.c_str(), L"wb");
|
2011-05-21 18:17:37 +00:00
|
|
|
if (f)
|
|
|
|
{
|
|
|
|
ret = (fwrite(coverList[0].data().data(), 1, size, f) == size);
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|