File indexing completed on 2024-05-05 04:48:36

0001 /****************************************************************************************
0002  * Copyright (c) 2010 Sergey Ivanov <123kash@gmail.com>                                 *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #define DEBUG_PREFIX "MusicDNSFinder"
0018 
0019 #include "MusicDNSFinder.h"
0020 
0021 #include "core/meta/Meta.h"
0022 #include "core/support/Debug.h"
0023 
0024 #include <ThreadWeaver/Queue>
0025 
0026 #include <QNetworkAccessManager>
0027 #include <QUrlQuery>
0028 
0029 MusicDNSFinder::MusicDNSFinder( QObject *parent,
0030                                 const QString &host, const int port, const QString &pathPrefix,
0031                                 const QString &clietnId, const QString &clientVersion )
0032                : QObject( parent )
0033                , mdns_host( host )
0034                , mdns_port( port )
0035                , mdns_pathPrefix( pathPrefix )
0036                , mdns_clientId( clietnId )
0037                , mdns_clientVersion( clientVersion )
0038 {
0039     DEBUG_BLOCK
0040     debug() << "Initiating MusicDNS search:";
0041     debug() << "\tHost:\t\t" << mdns_host;
0042     debug() << "\tPort:\t\t" << mdns_port;
0043     debug() << "\tPath Prefix:\t" << mdns_pathPrefix;
0044     debug() << "\tClient ID:\t" << mdns_clientId;
0045     debug() << "\tClient version:\t" << mdns_clientVersion;
0046     net = The::networkAccessManager();
0047 
0048     _timer = new QTimer( this );
0049     _timer->setInterval( 1000 );
0050 
0051     decodingComplete = false;
0052 
0053     connect( net, &NetworkAccessManagerProxy::finished, this, &MusicDNSFinder::gotReply );
0054     connect( _timer, &QTimer::timeout, this, &MusicDNSFinder::sendNewRequest );
0055 }
0056 
0057 void
0058 MusicDNSFinder::run( const Meta::TrackList &tracks )
0059 {
0060     MusicDNSAudioDecoder *decoder = new MusicDNSAudioDecoder( tracks );
0061     connect( decoder, &MusicDNSAudioDecoder::trackDecoded,
0062              this, &MusicDNSFinder::trackDecoded );
0063     connect( decoder, &MusicDNSAudioDecoder::done,
0064              this, &MusicDNSFinder::decodingDone );
0065 
0066     ThreadWeaver::Queue::instance()->enqueue( QSharedPointer<ThreadWeaver::Job>(decoder) );
0067 
0068     _timer->start();
0069 }
0070 
0071 void MusicDNSFinder::sendNewRequest()
0072 {
0073     DEBUG_BLOCK
0074     if( m_requests.isEmpty() )
0075     {
0076         checkDone();
0077         return;
0078     }
0079     QPair < Meta::TrackPtr, QNetworkRequest > req = m_requests.takeFirst();
0080     QNetworkReply *reply = net->get( req.second );
0081     m_replyes.insert( reply, req.first );
0082     connect( reply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::errorOccurred),
0083              this, &MusicDNSFinder::replyError );
0084     debug() << "Request sent: " << req.second.url().toString();
0085 }
0086 
0087 void
0088 MusicDNSFinder::gotReply( QNetworkReply *reply )
0089 {
0090     DEBUG_BLOCK
0091     if( reply->error() == QNetworkReply::NoError && m_replyes.contains( reply ) )
0092     {
0093         QString document( reply->readAll() );
0094         MusicDNSXmlParser *parser = new MusicDNSXmlParser( document );
0095         if( !m_replyes.value( reply ).isNull() )
0096             m_parsers.insert( parser, m_replyes.value( reply ) );
0097 
0098         connect( parser, &MusicDNSXmlParser::done, this, &MusicDNSFinder::parsingDone );
0099         ThreadWeaver::Queue::instance()->enqueue( QSharedPointer<ThreadWeaver::Job>(parser) );
0100     }
0101 
0102     m_replyes.remove( reply );
0103     reply->deleteLater();
0104     checkDone();
0105 }
0106 
0107 void
0108 MusicDNSFinder::replyError( QNetworkReply::NetworkError code )
0109 {
0110     DEBUG_BLOCK
0111     QNetworkReply *reply = qobject_cast< QNetworkReply * >( sender() );
0112     if( !reply )
0113         return;
0114 
0115     if( !m_replyes.contains( reply ) || code == QNetworkReply::NoError )
0116         return;
0117 
0118     disconnect( reply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::errorOccurred),
0119                 this, &MusicDNSFinder::replyError );
0120 
0121     debug() << "Error occurred during network request: " << reply->errorString();
0122     m_replyes.remove( reply );
0123     reply->deleteLater();
0124     checkDone();
0125 }
0126 
0127 void
0128 MusicDNSFinder::parsingDone( ThreadWeaver::JobPointer _parser )
0129 {
0130     DEBUG_BLOCK
0131 
0132     MusicDNSXmlParser *parser = dynamic_cast< MusicDNSXmlParser * >( _parser.data() );
0133     disconnect( parser, &MusicDNSXmlParser::done, this, &MusicDNSFinder::parsingDone );
0134     if( m_parsers.contains( parser ) )
0135     {
0136         bool found = false;
0137         foreach( QString PUID, parser->puid() )
0138             if( PUID != "00000000-0000-0000-0000-000000000000" )
0139             {
0140                 found = true;
0141                 emit trackFound( m_parsers.value( parser ), PUID );
0142                 break;
0143             }
0144 
0145         if( !found )
0146             emit progressStep();
0147 
0148         m_parsers.remove( parser );
0149     }
0150 
0151     parser->deleteLater();
0152     checkDone();
0153 }
0154 
0155 void
0156 MusicDNSFinder::trackDecoded( const Meta::TrackPtr track, const QString fingerprint )
0157 {
0158     DEBUG_BLOCK
0159     if( fingerprint.isEmpty() )
0160         return;
0161     m_requests.append( qMakePair( track, compileRequest( fingerprint, track ) ) );
0162 }
0163 
0164 void
0165 MusicDNSFinder::decodingDone( ThreadWeaver::JobPointer _decoder )
0166 {
0167     DEBUG_BLOCK
0168     MusicDNSAudioDecoder *decoder = dynamic_cast<MusicDNSAudioDecoder*>(_decoder.data());
0169     disconnect( decoder, &MusicDNSAudioDecoder::trackDecoded,
0170                 this, &MusicDNSFinder::trackDecoded );
0171     disconnect( decoder, &MusicDNSAudioDecoder::done,
0172                 this, &MusicDNSFinder::decodingDone );
0173     decoder->deleteLater();
0174     decodingComplete = true;
0175     checkDone();
0176 }
0177 
0178 void
0179 MusicDNSFinder::checkDone()
0180 {
0181     if( m_parsers.isEmpty() && m_requests.isEmpty() && m_replyes.isEmpty() && decodingComplete )
0182     {
0183         debug() << "There is no any queued requests. Stopping timer.";
0184         _timer->stop();
0185         emit done();
0186     }
0187 }
0188 
0189 QNetworkRequest
0190 MusicDNSFinder::compileRequest( const QString &fingerprint, const Meta::TrackPtr track )
0191 {
0192     QUrl url;
0193     QUrlQuery query;
0194     url.setScheme( "http" );
0195     url.setHost( mdns_host );
0196     url.setPort( mdns_port );
0197     url.setPath( mdns_pathPrefix+"/track/" );
0198     query.addQueryItem( "gnr", "" );
0199     query.addQueryItem( "art", track->artist().isNull()?"":track->artist()->name() );
0200     query.addQueryItem( "rmd", "0" );
0201     query.addQueryItem( "cid", mdns_clientId );
0202     query.addQueryItem( "alb", track->album().isNull()?"":track->album()->name() );
0203     query.addQueryItem( "fmt", "" );
0204     query.addQueryItem( "brt", QString::number( track->bitrate() ) );
0205     query.addQueryItem( "cvr", mdns_clientVersion );
0206     query.addQueryItem( "fpt", fingerprint );
0207     query.addQueryItem( "ttl", track->name().isNull()?track->playableUrl().fileName().remove(
0208                              QRegExp( "^.*(\\.+(?:\\w{2,5}))$" ) ):track->name() );
0209     query.addQueryItem( "tnm", "" );
0210     query.addQueryItem( "lkt", "" );
0211     query.addQueryItem( "dur", QString::number( track->length() ) );
0212     query.addQueryItem( "yrr", "" );
0213     url.setQuery( query );
0214 
0215     QNetworkRequest req( url );
0216     req.setRawHeader( "User-Agent" , "Amarok" );
0217     req.setRawHeader( "Connection", "Keep-Alive" );
0218 
0219     if( !_timer->isActive() )
0220         _timer->start();
0221 
0222     return req;
0223 }
0224