mirror of
https://github.com/chibicitiberiu/rainmeter-studio.git
synced 2024-02-24 04:33:31 +00:00
NowPlayingPlugin:
- Fixed that PlayerName=, TrackChangeAction=, and DisableLeadingZero= were global (i.e. only usable from the first loaded skin) - Code refactoring and cleanup
This commit is contained in:
332
Plugins/PluginNowPlaying/Cover.cpp
Normal file
332
Plugins/PluginNowPlaying/Cover.cpp
Normal file
@ -0,0 +1,332 @@
|
||||
/*
|
||||
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 "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
|
||||
**
|
||||
** Checks if cover art is in cache.
|
||||
**
|
||||
*/
|
||||
bool CCover::GetCachedCover(const std::wstring& artist, const std::wstring& title, std::wstring& target)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
** GetLocalArt
|
||||
**
|
||||
** Attemps to find local cover art in various formats.
|
||||
**
|
||||
*/
|
||||
bool CCover::GetLocalCover(std::wstring filename, const std::wstring& folder, std::wstring& target)
|
||||
{
|
||||
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)
|
||||
{
|
||||
target = testPath;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get rid of the added extension
|
||||
testPath.resize(origLen);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
** GetEmbeddedArt
|
||||
**
|
||||
** Attempts to extract cover art from audio files.
|
||||
**
|
||||
*/
|
||||
bool CCover::GetEmbeddedCover(const TagLib::FileRef& fr, std::wstring& target)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
if (TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(fr.file()))
|
||||
{
|
||||
if (file->ID3v2Tag())
|
||||
{
|
||||
found = ExtractID3(file->ID3v2Tag(), target);
|
||||
}
|
||||
if (!found && file->APETag())
|
||||
{
|
||||
found = ExtractAPE(file->APETag(), target);
|
||||
}
|
||||
}
|
||||
else if (TagLib::MP4::File* file = dynamic_cast<TagLib::MP4::File*>(fr.file()))
|
||||
{
|
||||
if (file->tag())
|
||||
{
|
||||
found = ExtractMP4(file, target);
|
||||
}
|
||||
}
|
||||
else if (TagLib::FLAC::File* file = dynamic_cast<TagLib::FLAC::File*>(fr.file()))
|
||||
{
|
||||
found = ExtractFLAC(file, target);
|
||||
|
||||
if (!found && file->ID3v2Tag())
|
||||
{
|
||||
found = ExtractID3(file->ID3v2Tag(), target);
|
||||
}
|
||||
}
|
||||
else if (TagLib::ASF::File* file = dynamic_cast<TagLib::ASF::File*>(fr.file()))
|
||||
{
|
||||
found = ExtractASF(file, target);
|
||||
}
|
||||
else if (TagLib::APE::File* file = dynamic_cast<TagLib::APE::File*>(fr.file()))
|
||||
{
|
||||
if (file->APETag())
|
||||
{
|
||||
found = ExtractAPE(file->APETag(), target);
|
||||
}
|
||||
}
|
||||
else if (TagLib::MPC::File* file = dynamic_cast<TagLib::MPC::File*>(fr.file()))
|
||||
{
|
||||
if (file->APETag())
|
||||
{
|
||||
found = ExtractAPE(file->APETag(), target);
|
||||
}
|
||||
}
|
||||
else if (TagLib::WavPack::File* file = dynamic_cast<TagLib::WavPack::File*>(fr.file()))
|
||||
{
|
||||
if (file->APETag())
|
||||
{
|
||||
found = ExtractAPE(file->APETag(), target);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/*
|
||||
** GetFileFolder
|
||||
**
|
||||
** Returns path without filename.
|
||||
**
|
||||
*/
|
||||
std::wstring CCover::GetFileFolder(const std::wstring& file)
|
||||
{
|
||||
std::wstring::size_type pos = file.find_last_of(L'\\');
|
||||
if (pos != std::wstring::npos)
|
||||
{
|
||||
return file.substr(0, ++pos);
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/*
|
||||
** ExtractAPE
|
||||
**
|
||||
** Extracts cover art embedded in APE tags.
|
||||
**
|
||||
*/
|
||||
bool CCover::ExtractAPE(TagLib::APE::Tag* tag, const std::wstring& target)
|
||||
{
|
||||
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);
|
||||
return WriteCover(pic, target);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
** ExtractID3
|
||||
**
|
||||
** Extracts cover art embedded in ID3v2 tags.
|
||||
**
|
||||
*/
|
||||
bool CCover::ExtractID3(TagLib::ID3v2::Tag* tag, const std::wstring& target)
|
||||
{
|
||||
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());
|
||||
return WriteCover(frame->picture(), target);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
** ExtractASF
|
||||
**
|
||||
** Extracts cover art embedded in ASF/WMA files.
|
||||
**
|
||||
*/
|
||||
bool CCover::ExtractASF(TagLib::ASF::File* file, const std::wstring& target)
|
||||
{
|
||||
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())
|
||||
{
|
||||
return WriteCover(wmpic.picture(), target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
** ExtractFLAC
|
||||
**
|
||||
** Extracts cover art embedded in FLAC files.
|
||||
**
|
||||
*/
|
||||
bool CCover::ExtractFLAC(TagLib::FLAC::File* file, const std::wstring& target)
|
||||
{
|
||||
const TagLib::List<TagLib::FLAC::Picture*>& picList = file->pictureList();
|
||||
if (!picList.isEmpty())
|
||||
{
|
||||
// Let's grab the first image
|
||||
TagLib::FLAC::Picture* pic = picList[0];
|
||||
return WriteCover(pic->data(), target);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
** ExtractMP4
|
||||
**
|
||||
** Extracts cover art embedded in MP4-like files.
|
||||
**
|
||||
*/
|
||||
bool CCover::ExtractMP4(TagLib::MP4::File* file, const std::wstring& target)
|
||||
{
|
||||
TagLib::MP4::Tag* tag = file->tag();
|
||||
if (tag->itemListMap().contains("covr"))
|
||||
{
|
||||
TagLib::MP4::CoverArtList coverList = tag->itemListMap()["covr"].toCoverArtList();
|
||||
if (coverList[0].data().size() > 0)
|
||||
{
|
||||
return WriteCover(coverList[0].data(), target);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
** WriteCover
|
||||
**
|
||||
** Write cover data to file.
|
||||
**
|
||||
*/
|
||||
bool CCover::WriteCover(const TagLib::ByteVector& data, const std::wstring& target)
|
||||
{
|
||||
bool written = false;
|
||||
|
||||
FILE* f = _wfopen(target.c_str(), L"wb");
|
||||
if (f)
|
||||
{
|
||||
written = (fwrite(data.data(), 1, data.size(), f) == data.size());
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
Reference in New Issue
Block a user