ktrm.cpp 19.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/**
 * Copyright (C) 2004 Scott Wheeler <wheeler@kde.org>
 * Copyright (C) 2007 Michael Pyne <mpyne@kde.org>
 */

/**
 *   This library is free software; you can redistribute it and/or modify
 *   it  under the terms of the GNU Lesser General Public License version
 *   2.1 as published by the Free Software Foundation.
 *
 *   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
 *   Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 *   02110-1301, USA
 */
21 22

#include "ktrm.h"
23
#include <config-juk.h>
24

25
#if HAVE_TUNEPIMP > 0
26 27 28 29

#include <kprotocolmanager.h>
#include <kurl.h>
#include <kdebug.h>
Laurent Montel's avatar
Laurent Montel committed
30
#include <kio/job.h>
31

32 33
#include <QCoreApplication>
#include <QtAlgorithms>
34
#include <QMutex>
35 36
#include <QRegExp>
#include <QFile>
37
#include <QEvent>
Laurent Montel's avatar
Laurent Montel committed
38
#include <QDomDocument>
39

Laurent Montel's avatar
Laurent Montel committed
40 41 42
#if HAVE_TUNEPIMP >= 5
#include <tunepimp-0.5/tp_c.h>
#else
43
#include <tunepimp/tp_c.h>
Laurent Montel's avatar
Laurent Montel committed
44
#endif
Benjamin Reed's avatar
Benjamin Reed committed
45
#ifdef Q_WS_X11
Stephan Kulow's avatar
Stephan Kulow committed
46
#include <fixx11h.h>
Benjamin Reed's avatar
Benjamin Reed committed
47
#endif
48 49 50 51 52

class KTRMLookup;

extern "C"
{
53
#if HAVE_TUNEPIMP >= 4
54 55
    static void TRMNotifyCallback(tunepimp_t pimp, void *data, TPCallbackEnum type, int fileId, TPFileStatus status);
#else
56
    static void TRMNotifyCallback(tunepimp_t pimp, void *data, TPCallbackEnum type, int fileId);
57
#endif
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
}

/**
 * This represents the main TunePimp instance and handles incoming requests.
 */

class KTRMRequestHandler
{
public:
    static KTRMRequestHandler *instance()
    {
        static QMutex mutex;
        mutex.lock();
        static KTRMRequestHandler handler;
        mutex.unlock();
        return &handler;
    }

76
    int startLookup(KTRMLookup *lookup)
77
    {
78 79 80
        int id;

        if(!m_fileMap.contains(lookup->file())) {
81
#if HAVE_TUNEPIMP >= 4
82 83
            id = tp_AddFile(m_pimp, QFile::encodeName(lookup->file()), 0);
#else
84
            id = tp_AddFile(m_pimp, QFile::encodeName(lookup->file()));
85
#endif
86 87 88 89 90 91
            m_fileMap.insert(lookup->file(), id);
        }
        else {
            id = m_fileMap[lookup->file()];
            tp_IdentifyAgain(m_pimp, id);
        }
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
        m_lookupMap[id] = lookup;
        return id;
    }

    void endLookup(KTRMLookup *lookup)
    {
        tp_ReleaseTrack(m_pimp, tp_GetTrack(m_pimp, lookup->fileId()));
        tp_Remove(m_pimp, lookup->fileId());
        m_lookupMap.remove(lookup->fileId());
    }

    bool lookupMapContains(int fileId) const
    {
        m_lookupMapMutex.lock();
        bool contains = m_lookupMap.contains(fileId);
        m_lookupMapMutex.unlock();
        return contains;
    }

    KTRMLookup *lookup(int fileId) const
    {
        m_lookupMapMutex.lock();
        KTRMLookup *l = m_lookupMap[fileId];
        m_lookupMapMutex.unlock();
        return l;
    }

    void removeFromLookupMap(int fileId)
    {
        m_lookupMapMutex.lock();
        m_lookupMap.remove(fileId);
        m_lookupMapMutex.unlock();
    }

    const tunepimp_t &tunePimp() const
    {
        return m_pimp;
    }

protected:
    KTRMRequestHandler()
    {
        m_pimp = tp_New("KTRM", "0.1");
135
        //tp_SetDebug(m_pimp, true);
Laurent Montel's avatar
Laurent Montel committed
136
#if HAVE_TUNEPIMP < 5
137
        tp_SetTRMCollisionThreshold(m_pimp, 100);
Laurent Montel's avatar
Laurent Montel committed
138 139
        tp_SetAutoFileLookup(m_pimp,true);
#endif        
140 141 142
        tp_SetAutoSaveThreshold(m_pimp, -1);
        tp_SetMoveFiles(m_pimp, false);
        tp_SetRenameFiles(m_pimp, false);
143
#if HAVE_TUNEPIMP >= 4
144 145
        tp_SetFileNameEncoding(m_pimp, "UTF-8");
#else
146
        tp_SetUseUTF8(m_pimp, true);
147
#endif
148
        tp_SetNotifyCallback(m_pimp, TRMNotifyCallback, 0);
Laurent Montel's avatar
Laurent Montel committed
149
#if HAVE_TUNEPIMP < 5
150 151 152
        // Re-read proxy config.
        KProtocolManager::reparseConfiguration();

153
        if(KProtocolManager::useProxy()) {
154 155
            // split code copied from kcm_kio.
            QString noProxiesFor = KProtocolManager::noProxyFor();
Pino Toscano's avatar
Pino Toscano committed
156
            QStringList noProxies = noProxiesFor.split(QRegExp("[',''\t'' ']"), QString::SkipEmptyParts);
157 158 159 160 161 162 163
            bool useProxy = true;

            // Host that libtunepimp will contact.
            QString tunepimpHost = "www.musicbrainz.org";
            QString tunepimpHostWithPort = "www.musicbrainz.org:80";

            // Check what hosts are allowed to proceed without being proxied,
164
            // or if using reversed proxy, what hosts must be proxied.
Albert Astals Cid's avatar
Albert Astals Cid committed
165
            foreach(const QString &host, noProxies) {
166
                const QString normalizedHost(KUrl::fromAce(KUrl::toAce(host)));
167 168

                if(normalizedHost == tunepimpHost ||
169
                   tunepimpHost.endsWith('.' + normalizedHost))
170 171 172 173 174 175 176 177
                {
                    useProxy = false;
                    break;
                }

                // KDE's proxy mechanism also supports exempting a specific
                // host/port combo, check that also.
                if(normalizedHost == tunepimpHostWithPort ||
178
                   tunepimpHostWithPort.endsWith('.' + normalizedHost))
179 180 181 182 183 184 185 186 187 188 189
                {
                    useProxy = false;
                    break;
                }
            }

            // KDE supports a reverse proxy mechanism.  Uh, yay.
            if(KProtocolManager::useReverseProxy())
                useProxy = !useProxy;

            if(useProxy) {
190
                KUrl proxy = KProtocolManager::proxyFor("http");
191 192
                QString proxyHost = proxy.host();

193
                kDebug() << "Using proxy server " << proxyHost << " for www.musicbrainz.org.\n";
Dirk Mueller's avatar
Dirk Mueller committed
194
                tp_SetProxy(m_pimp, proxyHost.toAscii(), short(proxy.port()));
195
            }
196
        }
Laurent Montel's avatar
Laurent Montel committed
197 198 199
#else
        tp_SetMusicDNSClientId(m_pimp, "0c6019606b1d8a54d0985e448f3603ca");
#endif
200 201 202 203 204 205 206 207 208
    }

    ~KTRMRequestHandler()
    {
        tp_Delete(m_pimp);
    }

private:
    tunepimp_t m_pimp;
209 210
    QMap<int, KTRMLookup *> m_lookupMap;
    QMap<QString, int> m_fileMap;
211 212 213 214 215 216 217 218
    mutable QMutex m_lookupMapMutex;
};


/**
 * A custom event type used for signalling that a TRM lookup is finished.
 */

219
class KTRMEvent : public QEvent
220 221 222 223 224 225
{
public:
    enum Status {
        Recognized,
        Unrecognized,
        Collision,
Laurent Montel's avatar
Laurent Montel committed
226
        PuidGenerated,
227 228 229 230
        Error
    };

    KTRMEvent(int fileId, Status status) :
231
        QEvent(static_cast<QEvent::Type>(id)),
232
        m_fileId(fileId),
233 234 235
        m_status(status)
    {
    }
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263

    int fileId() const
    {
        return m_fileId;
    }

    Status status() const
    {
        return m_status;
    }

    static const int id = User + 1984; // random, unique, event id

private:
    int m_fileId;
    Status m_status;
};

/**
 * A helper class to intercept KTRMQueryEvents and call recognized() (from the GUI
 * thread) for the lookup.
 */

class KTRMEventHandler : public QObject
{
public:
    static void send(int fileId, KTRMEvent::Status status)
    {
264
        QCoreApplication::postEvent(instance(), new KTRMEvent(fileId, status));
265 266 267 268 269 270 271 272 273 274 275 276 277 278
    }

protected:
    KTRMEventHandler() : QObject() {}

    static KTRMEventHandler *instance()
    {
        static QMutex mutex;
        mutex.lock();
        static KTRMEventHandler handler;
        mutex.unlock();
        return &handler;
    }

279
    virtual void customEvent(QEvent *event)
280
    {
281
        if(event->type() != KTRMEvent::id)
282 283 284 285 286 287 288 289 290 291 292 293 294
            return;

        KTRMEvent *e = static_cast<KTRMEvent *>(event);

        static QMutex mutex;
        mutex.lock();

        if(!KTRMRequestHandler::instance()->lookupMapContains(e->fileId())) {
            mutex.unlock();
            return;
        }

        KTRMLookup *lookup = KTRMRequestHandler::instance()->lookup(e->fileId());
295
#if HAVE_TUNEPIMP >= 4
296 297 298
        if ( e->status() != KTRMEvent::Unrecognized)
#endif
            KTRMRequestHandler::instance()->removeFromLookupMap(e->fileId());
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314

        mutex.unlock();

        switch(e->status()) {
        case KTRMEvent::Recognized:
            lookup->recognized();
            break;
        case KTRMEvent::Unrecognized:
            lookup->unrecognized();
            break;
        case KTRMEvent::Collision:
            lookup->collision();
            break;
        case KTRMEvent::Error:
            lookup->error();
            break;
315 316
        case KTRMEvent::PuidGenerated:
            break; // XXX: Does anything special need done here?
317 318 319 320 321
        }
    }
};

/**
322
 * Callback function for TunePimp lookup events.
323
 */
324
#if HAVE_TUNEPIMP >= 4
325
static void TRMNotifyCallback(tunepimp_t /*pimp*/, void * /*data*/, TPCallbackEnum type, int fileId, TPFileStatus status)
326 327 328
#else
static void TRMNotifyCallback(tunepimp_t pimp, void *data, TPCallbackEnum type, int fileId)
#endif
329 330 331 332
{
    if(type != tpFileChanged)
        return;

333
#if HAVE_TUNEPIMP < 4
334
    track_t track = tp_GetTrack(pimp, fileId);
335
    TPFileStatus status = tr_GetStatus(track);
336
#endif
337 338 339 340 341 342 343 344

    switch(status) {
    case eRecognized:
        KTRMEventHandler::send(fileId, KTRMEvent::Recognized);
        break;
    case eUnrecognized:
        KTRMEventHandler::send(fileId, KTRMEvent::Unrecognized);
        break;
Laurent Montel's avatar
Laurent Montel committed
345 346 347 348 349 350
#if HAVE_TUNEPIMP >= 5
    case ePUIDLookup:
    case ePUIDCollision:
    case eFileLookup:
        KTRMEventHandler::send(fileId, KTRMEvent::PuidGenerated);
        break;
351
#else
352
    case eTRMCollision:
353
#if HAVE_TUNEPIMP >= 4
354 355
    case eUserSelection:
#endif
356 357
        KTRMEventHandler::send(fileId, KTRMEvent::Collision);
        break;
Laurent Montel's avatar
Laurent Montel committed
358
#endif
359 360 361 362 363 364
    case eError:
        KTRMEventHandler::send(fileId, KTRMEvent::Error);
        break;
    default:
        break;
    }
365
#if HAVE_TUNEPIMP < 4
366 367
    tp_ReleaseTrack(pimp, track);
#endif
368 369
}

370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
////////////////////////////////////////////////////////////////////////////////
// KTRMResult implementation
////////////////////////////////////////////////////////////////////////////////

class KTRMResult::KTRMResultPrivate
{
public:
    KTRMResultPrivate() : track(0), year(0), relevance(0) {}
    QString title;
    QString artist;
    QString album;
    int track;
    int year;
    int relevance;
};

////////////////////////////////////////////////////////////////////////////////
// KTRMResult public methods
////////////////////////////////////////////////////////////////////////////////

KTRMResult::KTRMResult()
{
    d = new KTRMResultPrivate;
}

KTRMResult::KTRMResult(const KTRMResult &result)
{
    d = new KTRMResultPrivate(*result.d);
}

KTRMResult::~KTRMResult()
{
    delete d;
}

QString KTRMResult::title() const
{
    return d->title;
}

QString KTRMResult::artist() const
{
    return d->artist;
}

QString KTRMResult::album() const
{
    return d->album;
}

int KTRMResult::track() const
{
    return d->track;
}

int KTRMResult::year() const
{
    return d->year;
}

bool KTRMResult::operator<(const KTRMResult &r) const
{
    return r.d->relevance < d->relevance;
}

bool KTRMResult::operator>(const KTRMResult &r) const
{
    return r.d->relevance > d->relevance;
}

bool KTRMResult::isEmpty() const
{
    return d->title.isEmpty() && d->artist.isEmpty() && d->album.isEmpty() &&
        d->track == 0 && d->year == 0;
}

////////////////////////////////////////////////////////////////////////////////
// KTRMLookup implementation
////////////////////////////////////////////////////////////////////////////////

450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
class KTRMLookup::KTRMLookupPrivate
{
public:
    KTRMLookupPrivate() :
        fileId(-1) {}
    QString file;
    KTRMResultList results;
    int fileId;
    bool autoDelete;
};

////////////////////////////////////////////////////////////////////////////////
// KTRMLookup public methods
////////////////////////////////////////////////////////////////////////////////

KTRMLookup::KTRMLookup(const QString &file, bool autoDelete)
{
    d = new KTRMLookupPrivate;
    d->file = file;
    d->autoDelete = autoDelete;
    d->fileId = KTRMRequestHandler::instance()->startLookup(this);
}

KTRMLookup::~KTRMLookup()
{
    KTRMRequestHandler::instance()->endLookup(this);
    delete d;
}

QString KTRMLookup::file() const
{
    return d->file;
}

int KTRMLookup::fileId() const
{
    return d->fileId;
}

void KTRMLookup::recognized()
{
Thiago Macieira's avatar
Thiago Macieira committed
491
    kDebug() << d->file;
492 493 494 495 496

    d->results.clear();

    metadata_t *metaData = md_New();
    track_t track = tp_GetTrack(KTRMRequestHandler::instance()->tunePimp(), d->fileId);
497
    tr_Lock(track);
498 499 500 501
    tr_GetServerMetadata(track, metaData);

    KTRMResult result;

502 503 504 505 506
    result.d->title = QString::fromUtf8(metaData->track);
    result.d->artist = QString::fromUtf8(metaData->artist);
    result.d->album = QString::fromUtf8(metaData->album);
    result.d->track = metaData->trackNum;
    result.d->year = metaData->releaseYear;
507 508 509 510

    d->results.append(result);

    md_Delete(metaData);
511
    tr_Unlock(track);
512 513 514 515 516
    finished();
}

void KTRMLookup::unrecognized()
{
Thiago Macieira's avatar
Thiago Macieira committed
517
    kDebug() << d->file;
518
#if HAVE_TUNEPIMP >= 4
519 520 521 522 523
    char trm[255];
    bool finish = false;
    trm[0] = 0;
    track_t track = tp_GetTrack(KTRMRequestHandler::instance()->tunePimp(), d->fileId);
    tr_Lock(track);
Laurent Montel's avatar
Laurent Montel committed
524 525 526
#if HAVE_TUNEPIMP >= 5
    tr_GetPUID(track, trm, 255);
#else
527
    tr_GetTRM(track, trm, 255);
Laurent Montel's avatar
Laurent Montel committed
528 529
#endif

530 531 532 533 534 535 536 537 538 539 540
    if ( !trm[0] ) {
        tr_SetStatus(track, ePending);
        tp_Wake(KTRMRequestHandler::instance()->tunePimp(), track);
    }
    else
        finish = true;
    tr_Unlock(track);
    tp_ReleaseTrack(KTRMRequestHandler::instance()->tunePimp(), track);
    if ( !finish )
       return;
#endif
541 542 543 544 545 546
    d->results.clear();
    finished();
}

void KTRMLookup::collision()
{
547
#if HAVE_TUNEPIMP < 5
Thiago Macieira's avatar
Thiago Macieira committed
548
    kDebug() << d->file;
549 550 551 552

    track_t track = tp_GetTrack(KTRMRequestHandler::instance()->tunePimp(), d->fileId);

    if(track <= 0) {
553
        kDebug() << "invalid track number";
554 555 556 557 558 559
        return;
    }

    tr_Lock(track);
    int resultCount = tr_GetNumResults(track);

560
    if(resultCount > 0) {
561 562 563 564 565 566
        TPResultType type;
        result_t *results = new result_t[resultCount];
        tr_GetResults(track, &type, results, &resultCount);

        switch(type) {
        case eNone:
Thiago Macieira's avatar
Thiago Macieira committed
567
            kDebug() << "eNone";
568 569
            break;
        case eArtistList:
570
            kDebug() << "eArtistList";
571 572
            break;
        case eAlbumList:
573
            kDebug() << "eAlbumList";
574 575 576
            break;
        case eTrackList:
        {
577
            kDebug() << "eTrackList";
578 579 580 581 582 583
            albumtrackresult_t **tracks = (albumtrackresult_t **) results;
            d->results.clear();

            for(int i = 0; i < resultCount; i++) {
                KTRMResult result;

584
                result.d->title = QString::fromUtf8(tracks[i]->name);
585
#if HAVE_TUNEPIMP >= 4
586 587 588
                result.d->artist = QString::fromUtf8(tracks[i]->artist.name);
                result.d->album = QString::fromUtf8(tracks[i]->album.name);
                result.d->year = tracks[i]->album.releaseYear;
589
#else
590 591 592
                result.d->artist = QString::fromUtf8(tracks[i]->artist->name);
                result.d->album = QString::fromUtf8(tracks[i]->album->name);
                result.d->year = tracks[i]->album->releaseYear;
593 594
#endif
                result.d->track = tracks[i]->trackNum;
595
                result.d->relevance = tracks[i]->relevance;
596 597 598 599 600 601

                d->results.append(result);
            }
            break;
        }
        case eMatchedTrack:
Thiago Macieira's avatar
Thiago Macieira committed
602
            kDebug() << "eMatchedTrack";
603 604 605 606 607 608 609 610 611
            break;
        }

        delete [] results;
    }

    tr_Unlock(track);

    finished();
Laurent Montel's avatar
Laurent Montel committed
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
#endif
}

void KTRMLookup::puidGenerated()
{
#if HAVE_TUNEPIMP >= 5
    char puid[255] = {0};
    track_t track = tp_GetTrack(KTRMRequestHandler::instance()->tunePimp(), d->fileId);
    tr_Lock(track);

    tr_GetPUID(track, puid, 255);
    tr_Unlock(track);
    tp_ReleaseTrack(KTRMRequestHandler::instance()->tunePimp(), track);
    d->results.clear();

David Faure's avatar
David Faure committed
627
    KIO::Job *job = KIO::storedGet( QString( "http://musicbrainz.org/ws/1/track/?type=xml&puid=%1" ).arg( puid ) , KIO::NoReload, KIO::HideProgressInfo );
Laurent Montel's avatar
Laurent Montel committed
628
    connect( job, SIGNAL(result(KJob*)), SLOT(lookupResult(KJob*)) );
Laurent Montel's avatar
Laurent Montel committed
629 630 631 632 633 634
#endif
}

void KTRMLookup::lookupResult( KJob* job )
{
#if HAVE_TUNEPIMP >= 5
635
    if ( job->error() != 0 ) {
Laurent Montel's avatar
Laurent Montel committed
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
        finished();
        return;
    }
    KIO::StoredTransferJob* const storedJob = static_cast<KIO::StoredTransferJob*>( job );
    QString xml = QString::fromUtf8( storedJob->data().data(), storedJob->data().size() );

    QDomDocument doc;
    QDomElement e;

    if( !doc.setContent( xml ) ) {
        finished();
        return;
    }

    e = doc.namedItem( "metadata" ).toElement().namedItem( "track-list" ).toElement();

652
    QStringList strList = d->file.split('/');
Laurent Montel's avatar
Laurent Montel committed
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673

    QDomNode n = e.namedItem("track");
    for( ; !n.isNull();  n = n.nextSibling() ) {
        QDomElement track = n.toElement();
        KTRMResult result;

        result.d->title = track.namedItem( "title" ).toElement().text();
        result.d->artist = track.namedItem( "artist" ).toElement().namedItem( "name" ).toElement().text();
        QDomNode releaseNode = track.namedItem("release-list").toElement().namedItem("release");
        for( ; !releaseNode.isNull();  releaseNode = releaseNode.nextSibling() ) {
            KTRMResult tmpResult( result );
            QDomElement release = releaseNode.toElement();

            tmpResult.d->album = release.namedItem( "title" ).toElement().text();
            QDomNode tracklistN = release.namedItem( "track-list" );
            if ( !tracklistN.isNull() ) {
                QDomElement tracklist = tracklistN.toElement();
                if ( !tracklist.attribute( "offset" ).isEmpty() )
                    tmpResult.d->track = tracklist.attribute( "offset" ).toInt() + 1;
            }
            //tmpResult.d->year = ???;
674
            tmpResult.d->relevance = static_cast<int>(
Laurent Montel's avatar
Laurent Montel committed
675 676
                4 * stringSimilarity(strList,tmpResult.d->title) +
                2 * stringSimilarity(strList,tmpResult.d->artist) +
677 678 679
                1 * stringSimilarity(strList,tmpResult.d->album)
                );
/*
Laurent Montel's avatar
Laurent Montel committed
680 681 682 683 684 685
            if( !d->results.contains( tmpResult ) )
                d->results.append( tmpResult );
                */
        }
     }

686
     qSort(d->results);
Laurent Montel's avatar
Laurent Montel committed
687 688 689 690 691

     finished();
#else
    Q_UNUSED( job );
#endif
692 693
}

Laurent Montel's avatar
Laurent Montel committed
694

695 696
void KTRMLookup::error()
{
Thiago Macieira's avatar
Thiago Macieira committed
697
    kDebug() << d->file;
698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717

    d->results.clear();
    finished();
}

KTRMResultList KTRMLookup::results() const
{
    return d->results;
}

////////////////////////////////////////////////////////////////////////////////
// KTRMLookup protected methods
////////////////////////////////////////////////////////////////////////////////

void KTRMLookup::finished()
{
    if(d->autoDelete)
        delete this;
}

Laurent Montel's avatar
Laurent Montel committed
718 719 720
////////////////////////////////////////////////////////////////////////////////
// Helper Functions used for sorting MusicBrainz results
////////////////////////////////////////////////////////////////////////////////
721 722

double stringSimilarity(const QStringList &l, const QString &s)
Laurent Montel's avatar
Laurent Montel committed
723 724
{
    double max = 0, current = 0;
725

Albert Astals Cid's avatar
Albert Astals Cid committed
726
    foreach(const QString &string, l) {
727
        if(max < (current = stringSimilarity(string, s)))
Laurent Montel's avatar
Laurent Montel committed
728 729
            max = current;
    }
730

Laurent Montel's avatar
Laurent Montel committed
731 732
    return max;
}
733

Laurent Montel's avatar
Laurent Montel committed
734 735
double stringSimilarity(QString s1, QString s2)
{
736 737 738 739
    QRegExp whiteSpace("[\\s\\t\\r\\n]");

    s1.remove(whiteSpace);
    s2.remove(whiteSpace);
Laurent Montel's avatar
Laurent Montel committed
740 741 742 743 744 745 746 747

    double nCommon = 0;
    int p1 = 0, p2 = 0, x1 = 0, x2 = 0;
    int l1 = s1.length(), l2 = s2.length(), l3 = l1 + l2;
    QChar c1 = 0, c2 = 0;

    while(p1 < l1 && p2 < l2) {
        c1 = s1.at(p1); c2 = s2.at(p2);
748 749

        if(c1.toUpper() == c2.toUpper()) {
Laurent Montel's avatar
Laurent Montel committed
750 751 752 753
            ++nCommon;
            ++p1; ++p2;
        }
        else {
754 755
            x1 = s1.indexOf(c2, p1, Qt::CaseInsensitive);
            x2 = s2.indexOf(c1, p2, Qt::CaseInsensitive);
Laurent Montel's avatar
Laurent Montel committed
756 757 758 759 760 761 762

            if( (x1 == x2 || -1 == x1) || (-1 != x2 && x1 > x2) )
                ++p2;
            else
                ++p1;
        }
    }
763 764

    return l3 ? nCommon * 2.0 / l3 : 1.0;
Laurent Montel's avatar
Laurent Montel committed
765 766
}

767
#endif // (HAVE_TUNEPIMP > 0)
768

769
// vim: set et sw=4 tw=0 sta: