kdelibs/kcddb/libkcddb/musicbrainz/musicbrainzlookup.cpp
Ivailo Monev b4a354b74b generic: use QCryptographicHash instead of KMD5
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2016-04-01 04:32:22 +00:00

365 lines
12 KiB
C++

/*
Copyright (C) 2005-2007 Richard Lärkäng <nouseforaname@home.se>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "musicbrainzlookup.h"
#include <kdebug.h>
#include <klocale.h>
#include <qcryptographichash.h>
#include <cstdio>
#include <cstring>
#include <musicbrainz5/Query.h>
#include <musicbrainz5/Medium.h>
#include <musicbrainz5/Release.h>
#include <musicbrainz5/ReleaseGroup.h>
#include <musicbrainz5/Track.h>
#include <musicbrainz5/Recording.h>
#include <musicbrainz5/Disc.h>
#include <musicbrainz5/HTTPFetch.h>
#include <musicbrainz5/ArtistCredit.h>
#include <musicbrainz5/Artist.h>
#include <musicbrainz5/NameCredit.h>
#include <musicbrainz5/SecondaryType.h>
namespace KCDDB
{
MusicBrainzLookup::MusicBrainzLookup()
{
}
MusicBrainzLookup::~MusicBrainzLookup()
{
}
Result MusicBrainzLookup::lookup( const QString &, uint, const TrackOffsetList & trackOffsetList )
{
QString discId = calculateDiscId(trackOffsetList);
kDebug() << "Should lookup " << discId;
MusicBrainz5::CQuery Query("libkcddb-0.5");
// Code adapted from libmusicbrainz/examples/cdlookup.cc
try {
MusicBrainz5::CMetadata Metadata=Query.Query("discid",discId.toAscii().constData());
if (Metadata.Disc() && Metadata.Disc()->ReleaseList())
{
MusicBrainz5::CReleaseList *ReleaseList=Metadata.Disc()->ReleaseList();
kDebug() << "Found " << ReleaseList->NumItems() << " release(s)";
int relnr=1;
for (int i = 0; i < ReleaseList->NumItems(); i++)
{
MusicBrainz5::CRelease* Release=ReleaseList->Item(i);
//The releases returned from LookupDiscID don't contain full information
MusicBrainz5::CQuery::tParamMap Params;
Params["inc"]="artists labels recordings release-groups url-rels discids artist-credits";
std::string ReleaseID=Release->ID();
MusicBrainz5::CMetadata Metadata2=Query.Query("release",ReleaseID,"",Params);
if (Metadata2.Release())
{
MusicBrainz5::CRelease *FullRelease=Metadata2.Release();
//However, these releases will include information for all media in the release
//So we need to filter out the only the media we want.
MusicBrainz5::CMediumList MediaList=FullRelease->MediaMatchingDiscID(discId.toAscii().constData());
if (MediaList.NumItems() > 0)
{
/*if (FullRelease->ReleaseGroup())
kDebug() << "Release group title: " << FullRelease->ReleaseGroup()->Title();
else
kDebug() << "No release group for this release";*/
kDebug() << "Found " << MediaList.NumItems() << " media item(s)";
for (int i=0; i < MediaList.NumItems(); i++)
{
MusicBrainz5::CMedium* Medium= MediaList.Item(i);
/*kDebug() << "Found media: '" << Medium.Title() << "', position " << Medium.Position();*/
CDInfo info;
info.set(QLatin1String( "source" ), QLatin1String( "musicbrainz" ));
// Uses musicbrainz discid for the first release,
// then discid-2, discid-3 and so on, to
// allow multiple releases with the same discid
if (relnr == 1) {
info.set(QLatin1String( "discid" ), discId);
} else {
QString adjusted = discId;
adjusted.append(QLatin1String( "-" ));
adjusted.append(QString::number(relnr));
info.set(QLatin1String( "discid" ), adjusted);
}
QString title = QString::fromUtf8(FullRelease->Title().c_str());
if (FullRelease->MediumList()->NumItems() > 1)
title = i18n("%1 (disc %2)", title, Medium->Position());
info.set(Title, title);
info.set(Artist, artistFromCreditList(FullRelease->ArtistCredit()));
QString date = QString::fromUtf8(FullRelease->Date().c_str());
QRegExp yearRe("^(\\d{4,4})(-\\d{1,2}-\\d{1,2})?$");
int year = 0;
if (yearRe.indexIn(date) > -1)
{
QString yearString = yearRe.cap(1);
bool ok;
year=yearString.toInt(&ok);
if (!ok)
year = 0;
}
info.set(Year, year);
MusicBrainz5::CTrackList *TrackList=Medium->TrackList();
if (TrackList)
{
for (int i=0; i < TrackList->NumItems(); i++)
{
MusicBrainz5::CTrack* Track=TrackList->Item(i);
MusicBrainz5::CRecording *Recording=Track->Recording();
TrackInfo& track = info.track(i);
// Prefer title and artist from the track credits, but
// it appears to be empty if same as in Recording
// Noticable in the musicbrainztest-fulldate test,
// where the title on the credits of track 18 are
// "Bara om min älskade väntar", but the recording
// has title "Men bara om min älskade"
if(Recording && Track->ArtistCredit() == 0)
track.set(Artist, artistFromCreditList(Recording->ArtistCredit()));
else
track.set(Artist, artistFromCreditList(Track->ArtistCredit()));
if(Recording && Track->Title().empty())
track.set(Title, QString::fromUtf8(Recording->Title().c_str()));
else
track.set(Title, QString::fromUtf8(Track->Title().c_str()));
}
}
cdInfoList_ << info;
relnr++;
}
}
}
}
}
}
catch (MusicBrainz5::CConnectionError& Error)
{
kDebug() << "Connection Exception: '" << Error.what() << "'";
kDebug() << "LastResult: " << Query.LastResult();
kDebug() << "LastHTTPCode: " << Query.LastHTTPCode();
kDebug() << "LastErrorMessage: " << QString::fromUtf8(Query.LastErrorMessage().c_str());
return ServerError;
}
catch (MusicBrainz5::CTimeoutError& Error)
{
kDebug() << "Timeout Exception: '" << Error.what() << "'";
kDebug() << "LastResult: " << Query.LastResult();
kDebug() << "LastHTTPCode: " << Query.LastHTTPCode();
kDebug() << "LastErrorMessage: " << QString::fromUtf8(Query.LastErrorMessage().c_str());
return ServerError;
}
catch (MusicBrainz5::CAuthenticationError& Error)
{
kDebug() << "Authentication Exception: '" << Error.what() << "'";
kDebug() << "LastResult: " << Query.LastResult();
kDebug() << "LastHTTPCode: " << Query.LastHTTPCode();
kDebug() << "LastErrorMessage: " << QString::fromUtf8(Query.LastErrorMessage().c_str());
return ServerError;
}
catch (MusicBrainz5::CFetchError& Error)
{
kDebug() << "Fetch Exception: '" << Error.what() << "'";
kDebug() << "LastResult: " << Query.LastResult();
kDebug() << "LastHTTPCode: " << Query.LastHTTPCode();
kDebug() << "LastErrorMessage: " << QString::fromUtf8(Query.LastErrorMessage().c_str());
return ServerError;
}
catch (MusicBrainz5::CRequestError& Error)
{
kDebug() << "Request Exception: '" << Error.what() << "'";
kDebug() << "LastResult: " << Query.LastResult();
kDebug() << "LastHTTPCode: " << Query.LastHTTPCode();
kDebug() << "LastErrorMessage: " << QString::fromUtf8(Query.LastErrorMessage().c_str());
return ServerError;
}
catch (MusicBrainz5::CResourceNotFoundError& Error)
{
kDebug() << "ResourceNotFound Exception: '" << Error.what() << "'";
kDebug() << "LastResult: " << Query.LastResult();
kDebug() << "LastHTTPCode: " << Query.LastHTTPCode();
kDebug() << "LastErrorMessage: " << QString::fromUtf8(Query.LastErrorMessage().c_str());
return ServerError;
}
if (cdInfoList_.isEmpty())
{
kDebug() << "No record found";
return NoRecordFound;
}
kDebug() << "Query succeeded :-)";
return Success;
}
QString MusicBrainzLookup::calculateDiscId(const TrackOffsetList & trackOffsetList )
{
// Code based on libmusicbrainz/lib/diskid.cpp
int numTracks = trackOffsetList.count()-1;
QCryptographicHash sha(QCryptographicHash::Sha1);
char temp[9];
// FIXME How do I check that?
int firstTrack = 1;
int lastTrack = numTracks;
sprintf(temp, "%02X", firstTrack);
sha.addData(temp, strlen(temp));
sprintf(temp, "%02X", lastTrack);
sha.addData(temp, strlen(temp));
for(int i = 0; i < 100; i++)
{
long offset;
if (i == 0)
offset = trackOffsetList[numTracks];
else if (i <= numTracks)
offset = trackOffsetList[i-1];
else
offset = 0;
sprintf(temp, "%08lX", offset);
sha.addData(temp, strlen(temp));
}
QByteArray base64 = sha.result().toBase64();
// '/' '+' and '=' replaced for MusicBrainz
QString res = QString::fromLatin1(base64).replace(QLatin1Char( '/' ),QLatin1String( "_" )).replace(QLatin1Char( '+' ),QLatin1String( "." )).replace(QLatin1Char( '=' ),QLatin1String( "-" ));
return res;
}
CDInfoList MusicBrainzLookup::cacheFiles(const TrackOffsetList &offsetList, const Config& c )
{
CDInfoList infoList;
QStringList cddbCacheDirs = c.cacheLocations();
QString discid = calculateDiscId(offsetList);
for (QStringList::const_iterator cddbCacheDir = cddbCacheDirs.constBegin();
cddbCacheDir != cddbCacheDirs.constEnd(); ++cddbCacheDir)
{
// Looks for all files in cddbdir/musicbrainz/discid*
// Several files can correspond to the same discid,
// then they are named discid, discid-2, discid-3 and so on
QDir dir(*cddbCacheDir+QLatin1String( "/musicbrainz/" ));
dir.setNameFilters(QStringList(discid+QLatin1String( "*" )));
QStringList files = dir.entryList();
kDebug() << "Cache files found: " << files.count();
for (QStringList::iterator it = files.begin(); it != files.end(); ++it)
{
QFile f( dir.filePath(*it) );
if ( f.exists() && f.open(QIODevice::ReadOnly) )
{
QTextStream ts(&f);
ts.setCodec("UTF-8");
QString cddbData = ts.readAll();
f.close();
CDInfo info;
info.load(cddbData);
info.set(QLatin1String( "source" ), QLatin1String( "musicbrainz" ));
info.set(QLatin1String( "discid" ), discid);
infoList.append( info );
}
else
kDebug() << "Could not read file: " << f.fileName();
}
}
return infoList;
}
QString MusicBrainzLookup::artistFromCreditList(MusicBrainz5::CArtistCredit * artistCredit )
{
kDebug();
QString artistName;
MusicBrainz5::CNameCreditList *ArtistList=artistCredit->NameCreditList();
if (ArtistList)
{
for (int i=0; i < ArtistList->NumItems(); i++)
{
MusicBrainz5::CNameCredit* Name=ArtistList->Item(i);
MusicBrainz5::CArtist* Artist = Name->Artist();
if (!Name->Name().empty())
artistName += QString::fromUtf8(Name->Name().c_str());
else
artistName += QString::fromUtf8(Artist->Name().c_str());
artistName += QString::fromUtf8(Name->JoinPhrase().c_str());
}
kDebug() << "Artist:" << artistName;
}
return artistName;
}
}
// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
#include "moc_musicbrainzlookup.cpp"