File indexing completed on 2025-02-23 04:27:37
0001 /**************************************************************************************** 0002 * Copyright (c) 2006 Ian Monroe <ian@monroe.nu> * 0003 * Copyright (c) 2006 Seb Ruiz <ruiz@kde.org> * 0004 * Copyright (c) 2007 Maximilian Kossick <maximilian.kossick@googlemail.com> * 0005 * * 0006 * This program is free software; you can redistribute it and/or modify it under * 0007 * the terms of the GNU General Public License as published by the Free Software * 0008 * Foundation; either version 2 of the License, or (at your option) any later * 0009 * version. * 0010 * * 0011 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0012 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0013 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License along with * 0016 * this program. If not, see <http://www.gnu.org/licenses/>. * 0017 ****************************************************************************************/ 0018 0019 #define DEBUG_PREFIX "DaapCollection" 0020 0021 #include "DaapCollection.h" 0022 0023 #include "amarokconfig.h" 0024 #include "core/logger/Logger.h" 0025 #include "core/support/Components.h" 0026 #include "core/support/Debug.h" 0027 #include "DaapMeta.h" 0028 #include "MemoryQueryMaker.h" 0029 #include "daapreader/Reader.h" 0030 0031 #include <QStringList> 0032 #include <QTimer> 0033 0034 #include <KLocalizedString> 0035 0036 #include <DNSSD/RemoteService> 0037 #include <DNSSD/ServiceBase> 0038 #include <DNSSD/ServiceBrowser> 0039 0040 using namespace Collections; 0041 0042 DaapCollectionFactory::DaapCollectionFactory() 0043 : Collections::CollectionFactory() 0044 , m_browser( nullptr ) 0045 { 0046 } 0047 0048 DaapCollectionFactory::~DaapCollectionFactory() 0049 { 0050 delete m_browser; 0051 } 0052 0053 void 0054 DaapCollectionFactory::init() 0055 { 0056 DEBUG_BLOCK 0057 switch( KDNSSD::ServiceBrowser::isAvailable() ) 0058 { 0059 case KDNSSD::ServiceBrowser::Working: 0060 //don't block Amarok's startup by connecting to DAAP servers 0061 QTimer::singleShot( 1000, this, &DaapCollectionFactory::connectToManualServers ); 0062 m_browser = new KDNSSD::ServiceBrowser("_daap._tcp"); 0063 m_browser->setObjectName("daapServiceBrowser"); 0064 connect( m_browser, &KDNSSD::ServiceBrowser::serviceAdded, 0065 this, &DaapCollectionFactory::foundDaap ); 0066 connect( m_browser, &KDNSSD::ServiceBrowser::serviceRemoved, 0067 this, &DaapCollectionFactory::serverOffline ); 0068 m_browser->startBrowse(); 0069 break; 0070 0071 case KDNSSD::ServiceBrowser::Stopped: 0072 debug() << "The Zeroconf daemon is not running"; 0073 break; 0074 0075 case KDNSSD::ServiceBrowser::Unsupported: 0076 debug() << "Zeroconf support is not available"; 0077 break; 0078 0079 default: 0080 debug() << "Unknown error with Zeroconf"; 0081 } 0082 m_initialized = true; 0083 } 0084 0085 void 0086 DaapCollectionFactory::connectToManualServers() 0087 { 0088 DEBUG_BLOCK 0089 QStringList sl = AmarokConfig::manuallyAddedServers(); 0090 foreach( const QString &server, sl ) 0091 { 0092 debug() << "Adding server " << server; 0093 QStringList current = server.split( QLatin1Char(':'), Qt::KeepEmptyParts ); 0094 //handle invalid urls gracefully 0095 if( current.count() < 2 ) 0096 continue; 0097 0098 QString host = current.first(); 0099 quint16 port = current.last().toUShort(); 0100 Amarok::Logger::longMessage( 0101 i18n( "Loading remote collection from host %1", host), 0102 Amarok::Logger::Information ); 0103 0104 int lookup_id = QHostInfo::lookupHost( host, this, &DaapCollectionFactory::resolvedManualServerIp ); 0105 m_lookupHash.insert( lookup_id, port ); 0106 } 0107 } 0108 0109 void 0110 DaapCollectionFactory::serverOffline( KDNSSD::RemoteService::Ptr service ) 0111 { 0112 DEBUG_BLOCK 0113 QString key = serverKey( service->hostName(), service->port() ); 0114 if( m_collectionMap.contains( key ) ) 0115 { 0116 auto coll = m_collectionMap[ key ]; 0117 if( coll ) 0118 coll->serverOffline(); //collection will be deleted by collectionmanager 0119 else 0120 warning() << "collection already null"; 0121 0122 m_collectionMap.remove( key ); 0123 0124 } 0125 else 0126 warning() << "removing non-existent service"; 0127 } 0128 0129 void 0130 DaapCollectionFactory::foundDaap( KDNSSD::RemoteService::Ptr service ) 0131 { 0132 DEBUG_BLOCK 0133 0134 connect( service.data(), &KDNSSD::RemoteService::resolved, this, &DaapCollectionFactory::resolvedDaap ); 0135 service->resolveAsync(); 0136 } 0137 0138 void 0139 DaapCollectionFactory::resolvedDaap( bool success ) 0140 { 0141 const KDNSSD::RemoteService* service = dynamic_cast<const KDNSSD::RemoteService*>(sender()); 0142 if( !success || !service ) return; 0143 debug() << service->serviceName() << ' ' << service->hostName() << ' ' << service->domain() << ' ' << service->type(); 0144 0145 int lookup_id = QHostInfo::lookupHost( service->hostName(), this, &DaapCollectionFactory::resolvedServiceIp ); 0146 m_lookupHash.insert( lookup_id, service->port() ); 0147 } 0148 0149 QString 0150 DaapCollectionFactory::serverKey( const QString& host, quint16 port) const 0151 { 0152 return host + QLatin1Char(':') + QString::number( port ); 0153 } 0154 0155 void 0156 DaapCollectionFactory::slotCollectionReady() 0157 { 0158 DEBUG_BLOCK 0159 DaapCollection *collection = dynamic_cast<DaapCollection*>( sender() ); 0160 if( collection ) 0161 { 0162 disconnect( collection, &DaapCollection::remove, this, &DaapCollectionFactory::slotCollectionDownloadFailed ); 0163 Q_EMIT newCollection( collection ); 0164 } 0165 } 0166 0167 void 0168 DaapCollectionFactory::slotCollectionDownloadFailed() 0169 { 0170 DEBUG_BLOCK 0171 DaapCollection *collection = qobject_cast<DaapCollection*>( sender() ); 0172 if( !collection ) 0173 return; 0174 disconnect( collection, &DaapCollection::collectionReady, this, &DaapCollectionFactory::slotCollectionReady ); 0175 for( const auto &it : m_collectionMap ) 0176 { 0177 if( it.data() == collection ) 0178 { 0179 m_collectionMap.remove( m_collectionMap.key( it ) ); 0180 break; 0181 } 0182 } 0183 collection->deleteLater(); 0184 } 0185 0186 void 0187 DaapCollectionFactory::resolvedManualServerIp( const QHostInfo &hostInfo ) 0188 { 0189 if ( !m_lookupHash.contains(hostInfo.lookupId()) ) 0190 return; 0191 0192 if ( hostInfo.addresses().isEmpty() ) 0193 return; 0194 0195 QString host = hostInfo.hostName(); 0196 QString ip = hostInfo.addresses().at(0).toString(); 0197 quint16 port = m_lookupHash.value( hostInfo.lookupId() ); 0198 0199 //adding manual servers to the collectionMap doesn't make sense 0200 DaapCollection *coll = new DaapCollection( host, ip, port ); 0201 connect( coll, &DaapCollection::collectionReady, this, &DaapCollectionFactory::slotCollectionReady ); 0202 connect( coll, &DaapCollection::remove, this, &DaapCollectionFactory::slotCollectionDownloadFailed ); 0203 } 0204 0205 void 0206 DaapCollectionFactory::resolvedServiceIp( const QHostInfo &hostInfo ) 0207 { 0208 DEBUG_BLOCK 0209 // debug() << "got address:" << hostInfo.addresses() << "and lookup hash contains id" << hostInfo.lookupId() << "?" << m_lookupHash.contains(hostInfo.lookupId()); 0210 if ( !m_lookupHash.contains(hostInfo.lookupId()) ) 0211 return; 0212 0213 if ( hostInfo.addresses().isEmpty() ) 0214 return; 0215 0216 QString host = hostInfo.hostName(); 0217 QString ip = hostInfo.addresses().at(0).toString(); 0218 quint16 port = m_lookupHash.value( hostInfo.lookupId() ); 0219 0220 // debug() << "already added server?" << m_collectionMap.contains(serverKey( host, port )); 0221 if( m_collectionMap.contains(serverKey( host, port )) ) //same server from multiple interfaces 0222 return; 0223 0224 // debug() << "creating daap collection with" << host << ip << port; 0225 QPointer<DaapCollection> coll( new DaapCollection( host, ip, port ) ); 0226 connect( coll, &DaapCollection::collectionReady, this, &DaapCollectionFactory::slotCollectionReady ); 0227 connect( coll, &DaapCollection::remove, this, &DaapCollectionFactory::slotCollectionDownloadFailed ); 0228 m_collectionMap.insert( serverKey( host, port ), coll.data() ); 0229 } 0230 0231 //DaapCollection 0232 0233 DaapCollection::DaapCollection( const QString &host, const QString &ip, quint16 port ) 0234 : Collection() 0235 , m_host( host ) 0236 , m_port( port ) 0237 , m_ip( ip ) 0238 , m_reader( nullptr ) 0239 , m_mc( new MemoryCollection() ) 0240 { 0241 debug() << "Host: " << host << " port: " << port; 0242 m_reader = new Daap::Reader( this, host, port, QString(), this, "DaapReader" ); 0243 connect( m_reader, &Daap::Reader::passwordRequired,this, &DaapCollection::passwordRequired ); 0244 connect( m_reader, &Daap::Reader::httpError, this, &DaapCollection::httpError ); 0245 m_reader->loginRequest(); 0246 } 0247 0248 DaapCollection::~DaapCollection() 0249 { 0250 } 0251 0252 QueryMaker* 0253 DaapCollection::queryMaker() 0254 { 0255 return new MemoryQueryMaker( m_mc.toWeakRef(), collectionId() ); 0256 } 0257 0258 QString 0259 DaapCollection::collectionId() const 0260 { 0261 return QString( QStringLiteral("daap://") + m_ip + QLatin1Char(':') ) + QString::number( m_port ); 0262 } 0263 0264 QString 0265 DaapCollection::prettyName() const 0266 { 0267 QString host = m_host; 0268 // No need to be overly verbose 0269 if( host.endsWith( ".local" ) ) 0270 host = host.remove( QRegExp(".local$") ); 0271 return i18n("Music share at %1", host); 0272 } 0273 0274 void 0275 DaapCollection::passwordRequired() 0276 { 0277 //get password 0278 QString password; 0279 delete m_reader; 0280 m_reader = new Daap::Reader( this, m_host, m_port, password, this, "DaapReader" ); 0281 connect( m_reader, &Daap::Reader::passwordRequired, this, &DaapCollection::passwordRequired ); 0282 connect( m_reader, &Daap::Reader::httpError, this, &DaapCollection::httpError ); 0283 m_reader->loginRequest(); 0284 } 0285 0286 void 0287 DaapCollection::httpError( const QString &error ) 0288 { 0289 DEBUG_BLOCK 0290 debug() << "Http error in DaapReader: " << error; 0291 Q_EMIT remove(); 0292 } 0293 0294 void 0295 DaapCollection::serverOffline() 0296 { 0297 Q_EMIT remove(); 0298 } 0299 0300 void 0301 DaapCollection::loadedDataFromServer() 0302 { 0303 DEBUG_BLOCK 0304 Q_EMIT collectionReady(); 0305 } 0306 0307 void 0308 DaapCollection::parsingFailed() 0309 { 0310 DEBUG_BLOCK 0311 Q_EMIT remove(); 0312 }