diff --git a/src/services/ampache/AmpacheService.cpp b/src/services/ampache/AmpacheService.cpp index 0d1b45102c..daab047d12 100644 --- a/src/services/ampache/AmpacheService.cpp +++ b/src/services/ampache/AmpacheService.cpp @@ -1,136 +1,136 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * 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, see . * ****************************************************************************************/ #define DEBUG_PREFIX "AmpacheService" #include "AmpacheService.h" #include "AmpacheConfig.h" #include "AmpacheAccountLogin.h" #include "core/support/Amarok.h" #include "core/support/Components.h" #include "core/logger/Logger.h" #include "browsers/SingleCollectionTreeItemModel.h" #include "core-impl/collections/support/CollectionManager.h" #include #include "core/support/Debug.h" #ifdef HAVE_LIBLASTFM #include "LastfmInfoParser.h" #endif #include AmpacheServiceFactory::AmpacheServiceFactory() : ServiceFactory() {} void AmpacheServiceFactory::init() { //read config and create the needed number of services AmpacheConfig config; AmpacheServerList servers = config.servers(); m_initialized = true; for( int i = 0; i < servers.size(); i++ ) { AmpacheServerEntry server = servers.at( i ); ServiceBase* service = new AmpacheService( this, "Ampache (" + server.name + ')', server.url, server. username, server.password ); Q_EMIT newService( service ); } } QString AmpacheServiceFactory::name() { - return "Ampache"; + return QStringLiteral("Ampache"); } KConfigGroup AmpacheServiceFactory::config() { - return Amarok::config( "Service_Ampache" ); + return Amarok::config( QStringLiteral("Service_Ampache") ); } bool AmpacheServiceFactory::possiblyContainsTrack(const QUrl &url) const { AmpacheConfig config; foreach( const AmpacheServerEntry &server, config.servers() ) { if ( server.url.isParentOf( url ) ) return true; } return false; } AmpacheService::AmpacheService( AmpacheServiceFactory* parent, const QString & name, const QUrl &url, const QString &username, const QString &password ) : ServiceBase( name, parent ) - , m_infoParser( 0 ) - , m_collection( 0 ) + , m_infoParser( nullptr ) + , m_collection( nullptr ) , m_ampacheLogin( new AmpacheAccountLogin( url, username, password, this ) ) { DEBUG_BLOCK connect( m_ampacheLogin, &AmpacheAccountLogin::loginSuccessful, this, &AmpacheService::onLoginSuccessful ); setShortDescription( i18n( "Amarok frontend for your Ampache server" ) ); setIcon( QIcon::fromTheme( "view-services-ampache-amarok" ) ); setLongDescription( i18n( "Use Amarok as a seamless frontend to your Ampache server. This lets you browse and play all the Ampache contents from within Amarok." ) ); setImagePath( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/hover_info_ampache.png" ) ); #ifdef HAVE_LIBLASTFM m_infoParser = new LastfmInfoParser(); #endif } AmpacheService::~AmpacheService() { CollectionManager::instance()->removeTrackProvider( m_collection ); delete m_collection; m_ampacheLogin->deleteLater(); } void AmpacheService::polish() { m_bottomPanel->hide(); setInfoParser( m_infoParser ); /*if ( !m_authenticated ) authenticate( );*/ } void AmpacheService::reauthenticate() { m_ampacheLogin->reauthenticate(); // it would make sense here to clean the complete cache // information from a server might get outdated. } void AmpacheService::onLoginSuccessful() { m_collection = new Collections::AmpacheServiceCollection( this, m_ampacheLogin->server(), m_ampacheLogin->sessionId() ); // connect( m_collection, SIGNAL(authenticationNeeded()), SLOT(authenticate()) ); CollectionManager::instance()->addTrackProvider( m_collection ); QList levels; levels << CategoryId::Artist << CategoryId::Album; setModel( new SingleCollectionTreeItemModel( m_collection, levels ) ); setServiceReady( true ); } diff --git a/src/services/ampache/AmpacheServiceCollection.cpp b/src/services/ampache/AmpacheServiceCollection.cpp index ed473f8c48..51a878a469 100644 --- a/src/services/ampache/AmpacheServiceCollection.cpp +++ b/src/services/ampache/AmpacheServiceCollection.cpp @@ -1,196 +1,196 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * 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, see . * ****************************************************************************************/ #include "AmpacheServiceCollection.h" #include "AmpacheServiceQueryMaker.h" #include "NetworkAccessManagerProxy.h" #include #include #include #include #include #include using namespace Collections; AmpacheServiceCollection::AmpacheServiceCollection( ServiceBase *service, const QUrl &server, const QString &sessionId ) : ServiceCollection( service, "AmpacheCollection", "AmpacheCollection" ) , m_server( server ) , m_sessionId( sessionId ) { - m_trackForUrlWorker = 0; + m_trackForUrlWorker = nullptr; } AmpacheServiceCollection::~AmpacheServiceCollection() { } QueryMaker * AmpacheServiceCollection::queryMaker() { return new AmpacheServiceQueryMaker( this, m_server, m_sessionId ); } QString AmpacheServiceCollection::collectionId() const { return "Ampache: " + m_server.url(); } QString AmpacheServiceCollection::prettyName() const { return i18n( "Ampache Server %1", m_server.url() ); } bool AmpacheServiceCollection::possiblyContainsTrack( const QUrl &url ) const { return m_server.isParentOf( url ); } void AmpacheServiceCollection::slotAuthenticationNeeded() { Q_EMIT authenticationNeeded(); } Meta::TrackPtr AmpacheServiceCollection::trackForUrl( const QUrl &url ) { MetaProxy::TrackPtr trackptr( new MetaProxy::Track( url, MetaProxy::Track::ManualLookup ) ); AmpacheTrackForUrlWorker *worker = new AmpacheTrackForUrlWorker( url, trackptr, m_server, m_sessionId, service() ); connect( worker, &AmpacheTrackForUrlWorker::authenticationNeeded, this, &AmpacheServiceCollection::slotAuthenticationNeeded ); ThreadWeaver::Queue::instance()->enqueue( QSharedPointer(worker) ); return Meta::TrackPtr::staticCast( trackptr ); } void AmpacheServiceCollection::slotLookupComplete( const Meta::TrackPtr& ) { } void AmpacheTrackForUrlWorker::parseTrack( const QString &xml ) { //so lets figure out what we got here: QDomDocument doc( "reply" ); doc.setContent( xml ); QDomElement root = doc.firstChildElement( "root" ); QDomElement song = root.firstChildElement( "song" ); m_urlTrackId = song.attribute( "id", "0" ).toInt(); QDomElement element = song.firstChildElement( "title" ); QString title = element.text(); if ( title.isEmpty() ) title = "Unknown"; element = song.firstChildElement( "url" ); m_urlTrack = new Meta::AmpacheTrack( title, m_service ); Meta::TrackPtr trackPtr( m_urlTrack ); m_urlTrack->setUidUrl( element.text() ); m_urlTrack->setId( m_urlTrackId ); element = song.firstChildElement( "time" ); m_urlTrack->setLength( element.text().toInt() * 1000 ); element = song.firstChildElement( "track" ); m_urlTrack->setTrackNumber( element.text().toInt() ); QDomElement albumElement = song.firstChildElement( "album" ); m_urlAlbumId = albumElement.attribute( "id", "0" ).toInt(); Meta::AmpacheAlbum *album = new Meta::AmpacheAlbum( albumElement.text() ); QDomElement artElement = song.firstChildElement( "art" ); album->setCoverUrl( artElement.text() ); album->addTrack( trackPtr ); m_urlTrack->setAlbumPtr( Meta::AlbumPtr( album ) ); QDomElement artistElement = song.firstChildElement( "artist" ); Meta::ServiceArtist *artist = new Meta::ServiceArtist( artistElement.text() ); Meta::ArtistPtr artistPtr( artist ); m_urlTrack->setArtist( artistPtr ); album->setAlbumArtist( artistPtr ); } AmpacheTrackForUrlWorker::AmpacheTrackForUrlWorker( const QUrl &url, const MetaProxy::TrackPtr &track, const QUrl &server, const QString &sessionId, ServiceBase *service ) : Amarok::TrackForUrlWorker( url ) , m_proxy( track ) , m_server( server ) , m_sessionId( sessionId ) , m_service( service ) { } AmpacheTrackForUrlWorker::~AmpacheTrackForUrlWorker() {} void AmpacheTrackForUrlWorker::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) { Q_UNUSED( self ) Q_UNUSED( thread ) - m_urlTrack = 0; - m_urlAlbum = 0; - m_urlArtist = 0; + m_urlTrack = nullptr; + m_urlAlbum = nullptr; + m_urlArtist = nullptr; m_urlTrackId = 0; m_urlAlbumId = 0; m_urlArtistId = 0; //send url_to_song to Ampache QUrl requestUrl = m_server; - requestUrl.setPath( m_server.path() + "/server/xml.server.php" ); + requestUrl.setPath( m_server.path() + QStringLiteral("/server/xml.server.php") ); QUrlQuery query; query.addQueryItem( "action", "url_to_song" ); query.addQueryItem( "auth", m_sessionId ); query.addQueryItem( "url", m_url.toEncoded() ); requestUrl.setQuery( query ); QNetworkRequest req( requestUrl ); QNetworkReply *reply = The::networkAccessManager()->get( req ); if( reply->waitForReadyRead(-1) ) { if( reply->error() == QNetworkReply::ContentAccessDenied ) { debug() << "Trying to re-authenticate Ampache.."; Q_EMIT authenticationNeeded(); } } parseTrack( reply->readAll() ); m_track = Meta::TrackPtr( m_urlTrack ); m_proxy->updateTrack( m_track ); reply->deleteLater(); } diff --git a/src/services/ampache/AmpacheServiceQueryMaker.cpp b/src/services/ampache/AmpacheServiceQueryMaker.cpp index b4ffb701c1..943dd2af6e 100644 --- a/src/services/ampache/AmpacheServiceQueryMaker.cpp +++ b/src/services/ampache/AmpacheServiceQueryMaker.cpp @@ -1,751 +1,751 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * Copyright (c) 2007 Adam Pigg * * Copyright (c) 2007 Casey Link * * (c) 2013 Ralf Engels * * * * 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, see . * ****************************************************************************************/ #define DEBUG_PREFIX "AmpacheServiceQueryMaker" #include "AmpacheServiceQueryMaker.h" #include "AmpacheMeta.h" #include "core/meta/Statistics.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include "core/meta/support/MetaConstants.h" #include "core-impl/collections/support/MemoryMatcher.h" #include #include #include #include using namespace Collections; struct AmpacheServiceQueryMaker::Private { AmpacheServiceCollection* collection; QueryMaker::QueryType type; int maxsize; QAtomicInt expectedReplies; QUrl server; QString sessionId; QList parentTrackIds; QList parentAlbumIds; QList parentArtistIds; uint dateFilter; QString artistFilter; QString albumFilter; /** We are collecting the results of the queries and submit them in one block to ensure that we don't report albums twice and because the CollectionTreeItemModelBase does not handle multiple results correctly (which it should). */ Meta::AlbumList albumResults; Meta::ArtistList artistResults; Meta::TrackList trackResults; }; AmpacheServiceQueryMaker::AmpacheServiceQueryMaker( AmpacheServiceCollection * collection, const QUrl &server, const QString &sessionId ) : DynamicServiceQueryMaker() , d( new Private ) { d->collection = collection; d->type = QueryMaker::None; d->maxsize = 0; d->server = server; d->sessionId = sessionId; d->dateFilter = 0; } AmpacheServiceQueryMaker::~AmpacheServiceQueryMaker() { delete d; } void AmpacheServiceQueryMaker::run() { DEBUG_BLOCK if( d->expectedReplies ) // still running an old query return; //naive implementation, fix this //note: we are not handling filtering yet d->collection->acquireReadLock(); if ( d->type == QueryMaker::Artist ) fetchArtists(); else if( d->type == QueryMaker::Album ) fetchAlbums(); else if( d->type == QueryMaker::Track ) fetchTracks(); else warning() << "Requested unhandled query type"; //TODO error handling d->collection->releaseLock(); } void AmpacheServiceQueryMaker::abortQuery() { } QueryMaker * AmpacheServiceQueryMaker::setQueryType( QueryType type ) { d->type = type; return this; } QueryMaker* AmpacheServiceQueryMaker::addMatch( const Meta::TrackPtr &track ) { DEBUG_BLOCK const Meta::AmpacheTrack* serviceTrack = dynamic_cast< const Meta::AmpacheTrack * >( track.data() ); if( serviceTrack ) { d->parentTrackIds << serviceTrack->id(); debug() << "parent id set to: " << d->parentTrackIds; } else { // searching for something from another collection //hmm, not sure what to do now } return this; } QueryMaker* AmpacheServiceQueryMaker::addMatch( const Meta::ArtistPtr &artist, ArtistMatchBehaviour behaviour ) { Q_UNUSED( behaviour ) // TODO DEBUG_BLOCK if( d->parentAlbumIds.isEmpty() ) { const Meta::AmpacheArtist* serviceArtist = dynamic_cast< const Meta::AmpacheArtist * >( artist.data() ); if( serviceArtist ) { d->parentArtistIds << serviceArtist->id(); } else { // searching for something from another collection if( d->collection->artistMap().contains( artist->name() ) ) { serviceArtist = static_cast< const Meta::AmpacheArtist* >( d->collection->artistMap().value( artist->name() ).data() ); d->parentArtistIds << serviceArtist->id(); } else { //hmm, not sure what to do now } } } return this; } QueryMaker * AmpacheServiceQueryMaker::addMatch( const Meta::AlbumPtr & album ) { DEBUG_BLOCK const Meta::AmpacheAlbum* serviceAlbum = dynamic_cast< const Meta::AmpacheAlbum * >( album.data() ); if( serviceAlbum ) { d->parentAlbumIds << serviceAlbum->ids(); debug() << "parent id set to: " << d->parentAlbumIds; d->parentArtistIds.clear(); } else { // searching for something from another collection if( d->collection->albumMap().contains( album ) ) // compares albums by value { serviceAlbum = static_cast< const Meta::AmpacheAlbum* >( d->collection->albumMap().value( album ).data() ); d->parentAlbumIds << serviceAlbum->ids(); d->parentArtistIds.clear(); } else { //hmm, not sure what to do now } } return this; } void AmpacheServiceQueryMaker::fetchArtists() { DEBUG_BLOCK Meta::ArtistList artists; // first try the cache if( !d->parentArtistIds.isEmpty() ) { - foreach( int artistId, d->parentArtistIds ) + for( int artistId : qAsConst(d->parentArtistIds) ) artists << d->collection->artistById( artistId ); } if( !artists.isEmpty() ) { debug() << "got" << artists.count() << "artists from the memory collection"; Q_EMIT newArtistsReady( artists ); Q_EMIT queryDone(); return; } QUrl request = getRequestUrl( "artists" ); QUrlQuery query( request ); if ( !d->artistFilter.isEmpty() ) { query.addQueryItem( "filter", d->artistFilter ); request.setQuery( query ); } d->expectedReplies.ref(); The::networkAccessManager()->getData( request, this, &AmpacheServiceQueryMaker::artistDownloadComplete ); } void AmpacheServiceQueryMaker::fetchAlbums() { DEBUG_BLOCK Meta::AlbumList albums; // first try the cache if( !d->parentArtistIds.isEmpty() ) { foreach( int artistId, d->parentArtistIds ) albums << matchAlbums( d->collection, d->collection->artistById( artistId ) ); } if( !albums.isEmpty() ) { debug() << "got" << albums.count() << "albums from the memory collection"; Q_EMIT newAlbumsReady( albums ); Q_EMIT queryDone(); return; } if( !d->parentArtistIds.isEmpty() ) { foreach( int id, d->parentArtistIds ) { QUrl request = getRequestUrl( "artist_albums" ); QUrlQuery query( request ); query.addQueryItem( "filter", QString::number( id ) ); request.setQuery( query ); d->expectedReplies.ref(); The::networkAccessManager()->getData( request, this, &AmpacheServiceQueryMaker::albumDownloadComplete ); } } else { QUrl request = getRequestUrl( "albums" ); QUrlQuery query( request ); if ( !d->albumFilter.isEmpty() ) { query.addQueryItem( "filter", d->albumFilter ); request.setQuery( query ); } d->expectedReplies.ref(); The::networkAccessManager()->getData( request, this, &AmpacheServiceQueryMaker::albumDownloadComplete ); } } void AmpacheServiceQueryMaker::fetchTracks() { DEBUG_BLOCK Meta::TrackList tracks; //debug() << "parent album id: " << d->parentAlbumId; // first try the cache // TODO: this is fishy as we cannot be sure that the cache contains // everything // we should cache database query results instead if( !d->parentTrackIds.isEmpty() ) { foreach( int trackId, d->parentTrackIds ) { tracks << d->collection->trackById( trackId ); } } else if( !d->parentAlbumIds.isEmpty() ) { foreach( int albumId, d->parentAlbumIds ) { AlbumMatcher albumMatcher( d->collection->albumById( albumId ) ); tracks << albumMatcher.match( d->collection->trackMap().values() ); } } else if( d->parentArtistIds.isEmpty() ) { foreach( int artistId, d->parentArtistIds ) { ArtistMatcher artistMatcher( d->collection->artistById( artistId ) ); tracks << artistMatcher.match( d->collection->trackMap().values() ); } } if( !tracks.isEmpty() ) { debug() << "got" << tracks.count() << "tracks from the memory collection"; Q_EMIT newTracksReady( tracks ); Q_EMIT queryDone(); return; } QUrl request = getRequestUrl(); if( !d->parentAlbumIds.isEmpty() ) { foreach( int id, d->parentAlbumIds ) { QUrl request = getRequestUrl( "album_songs" ); QUrlQuery query( request ); query.addQueryItem( "filter", QString::number( id ) ); request.setQuery( query ); d->expectedReplies.ref(); The::networkAccessManager()->getData( request, this, &AmpacheServiceQueryMaker::trackDownloadComplete ); } } else if( !d->parentArtistIds.isEmpty() ) { foreach( int id, d->parentArtistIds ) { QUrl request = getRequestUrl( "artist_songs" ); QUrlQuery query( request ); query.addQueryItem( "filter", QString::number( id ) ); request.setQuery( query ); d->expectedReplies.ref(); The::networkAccessManager()->getData( request, this, &AmpacheServiceQueryMaker::trackDownloadComplete ); } } else { QUrl request = getRequestUrl( "songs" ); d->expectedReplies.ref(); The::networkAccessManager()->getData( request, this, &AmpacheServiceQueryMaker::trackDownloadComplete ); } } void AmpacheServiceQueryMaker::artistDownloadComplete( const QUrl &url, const QByteArray &data, const NetworkAccessManagerProxy::Error &e ) { Q_UNUSED( url ); if( e.code != QNetworkReply::NoError ) { warning() << "Artist download error:" << e.description; if( !d->expectedReplies.deref() ) Q_EMIT queryDone(); return; } // DEBUG_BLOCK // so lets figure out what we got here: QDomDocument doc( "reply" ); doc.setContent( data ); QDomElement root = doc.firstChildElement( "root" ); // Is this an error, if so we need to 'un-ready' the service and re-authenticate before continuing QDomElement domError = root.firstChildElement( "error" ); if ( !domError.isNull() ) { warning() << "Error getting Artist List" << domError.text() << "Code:" << domError.attribute("code"); AmpacheService *parentService = dynamic_cast< AmpacheService * >( d->collection->service() ); if( !parentService ) return; else parentService->reauthenticate(); } for( QDomNode n = root.firstChild(); !n.isNull(); n = n.nextSibling() ) { QDomElement e = n.toElement(); // try to convert the node to an element. QDomElement element = n.firstChildElement( "name" ); int artistId = e.attribute( "id", "0").toInt(); // check if we have the artist already Meta::ArtistPtr artistPtr = d->collection->artistById( artistId ); if( !artistPtr ) { // new artist Meta::ServiceArtist* artist = new Meta::AmpacheArtist( element.text(), d->collection->service() ); artist->setId( artistId ); // debug() << "Adding artist: " << element.text() << " with id: " << artistId; artistPtr = artist; d->collection->acquireWriteLock(); d->collection->addArtist( artistPtr ); d->collection->releaseLock(); } if( !d->artistResults.contains( artistPtr ) ) d->artistResults.push_back( artistPtr ); } if( !d->expectedReplies.deref() ) { Q_EMIT newArtistsReady( d->artistResults ); Q_EMIT queryDone(); d->artistResults.clear(); } } void AmpacheServiceQueryMaker::albumDownloadComplete( const QUrl &url, const QByteArray &data, const NetworkAccessManagerProxy::Error &e ) { Q_UNUSED( url ); if( e.code != QNetworkReply::NoError ) { warning() << "Album download error:" << e.description; if( !d->expectedReplies.deref() ) Q_EMIT queryDone(); return; } // DEBUG_BLOCK //so lets figure out what we got here: QDomDocument doc( "reply" ); doc.setContent( data ); QDomElement root = doc.firstChildElement( "root" ); // Is this an error, if so we need to 'un-ready' the service and re-authenticate before continuing QDomElement domError = root.firstChildElement( "error" ); if( !domError.isNull() ) { warning() << "Error getting Album List" << domError.text() << "Code:" << domError.attribute("code"); AmpacheService *parentService = dynamic_cast< AmpacheService * >(d->collection->service()); - if( parentService == 0 ) + if( parentService == nullptr ) return; else parentService->reauthenticate(); } for( QDomNode n = root.firstChild(); !n.isNull(); n = n.nextSibling() ) { QDomElement e = n.toElement(); // try to convert the node to an element. // --- the album artist Meta::ArtistPtr artistPtr; QDomElement artistElement = n.firstChildElement( "artist" ); if( !artistElement.isNull() ) { int artistId = artistElement.attribute( "id", "0").toInt(); // check if we already know the artist artistPtr = d->collection->artistById( artistId ); if( !artistPtr.data() ) { // new artist. Meta::ServiceArtist* artist = new Meta::AmpacheArtist( artistElement.text(), d->collection->service() ); artistPtr = artist; artist->setId( artistId ); // debug() << "Adding artist: " << artistElement.text() << " with id: " << artistId; d->collection->acquireWriteLock(); d->collection->addArtist( artistPtr ); d->collection->releaseLock(); } } QDomElement element = n.firstChildElement( "name" ); QString title = element.text(); Meta::AmpacheAlbum::AmpacheAlbumInfo info; info.id = e.attribute( "id", "0" ).toInt(); element = n.firstChildElement( "disk" ); info.discNumber = element.text().toInt(); element = n.firstChildElement( "year" ); info.year = element.text().toInt(); // check if we have the album already Meta::AlbumPtr albumPtr = d->collection->albumById( info.id ); if( !albumPtr ) { // check if we at least have an album with the same title and artist Meta::AmpacheAlbum* album = static_cast( const_cast( d->collection->albumMap().value( title, artistPtr ? artistPtr->name() : QString() ).data() ) ); if( !album ) { // new album album = new Meta::AmpacheAlbum( title ); album->setAlbumArtist( artistPtr ); // -- cover element = n.firstChildElement( "art" ); QString coverUrl = element.text(); album->setCoverUrl( coverUrl ); } album->addInfo( info ); // debug() << "Adding album" << title << "with id:" << info.id; albumPtr = album; // register a new id with the ServiceCollection album->setId( info.id ); d->collection->acquireWriteLock(); d->collection->addAlbum( albumPtr ); d->collection->releaseLock(); } if( !d->albumResults.contains( albumPtr ) ) d->albumResults.push_back( albumPtr ); } if( !d->expectedReplies.deref() ) { Q_EMIT newAlbumsReady( d->albumResults ); Q_EMIT queryDone(); d->albumResults.clear(); } } void AmpacheServiceQueryMaker::trackDownloadComplete( const QUrl &url, const QByteArray &data, const NetworkAccessManagerProxy::Error &e ) { Q_UNUSED( url ); if( e.code != QNetworkReply::NoError ) { warning() << "Track download error:" << e.description; if( !d->expectedReplies.deref() ) Q_EMIT queryDone(); return; } // DEBUG_BLOCK //so lets figure out what we got here: QDomDocument doc( "reply" ); doc.setContent( data ); QDomElement root = doc.firstChildElement( "root" ); // Is this an error, if so we need to 'un-ready' the service and re-authenticate before continuing QDomElement domError = root.firstChildElement( "error" ); if( !domError.isNull() ) { warning() << "Error getting Track Download " << domError.text() << "Code:" << domError.attribute("code"); AmpacheService *parentService = dynamic_cast< AmpacheService * >( d->collection->service() ); - if( parentService == 0 ) + if( parentService == nullptr ) return; else parentService->reauthenticate(); } for( QDomNode n = root.firstChild(); !n.isNull(); n = n.nextSibling() ) { QDomElement e = n.toElement(); // try to convert the node to an element. int trackId = e.attribute( "id", "0" ).toInt(); Meta::TrackPtr trackPtr = d->collection->trackById( trackId ); if( !trackPtr ) { // new track QDomElement element = n.firstChildElement( "title" ); QString title = element.text(); Meta::AmpacheTrack * track = new Meta::AmpacheTrack( title, d->collection->service() ); trackPtr = track; track->setId( trackId ); element = n.firstChildElement( "url" ); track->setUidUrl( element.text() ); element = n.firstChildElement( "time" ); track->setLength( element.text().toInt() * 1000 ); element = n.firstChildElement( "track" ); track->setTrackNumber( element.text().toInt() ); element = n.firstChildElement( "rating" ); track->statistics()->setRating( element.text().toDouble() * 2.0 ); QDomElement albumElement = n.firstChildElement( "album" ); int albumId = albumElement.attribute( "id", "0").toInt(); QDomElement artistElement = n.firstChildElement( "artist" ); int artistId = artistElement.attribute( "id", "0").toInt(); Meta::ArtistPtr artistPtr = d->collection->artistById( artistId ); // TODO: this assumes that we query all artist before tracks if( artistPtr ) { // debug() << "Found parent artist " << artistPtr->name(); Meta::ServiceArtist *artist = dynamic_cast< Meta::ServiceArtist * > ( artistPtr.data() ); track->setArtist( artistPtr ); artist->addTrack( trackPtr ); } Meta::AlbumPtr albumPtr = d->collection->albumById( albumId ); // TODO: this assumes that we query all albums before tracks if( albumPtr ) { // debug() << "Found parent album " << albumPtr->name() << albumId; Meta::AmpacheAlbum *album = dynamic_cast< Meta::AmpacheAlbum * > ( albumPtr.data() ); track->setDiscNumber( album->getInfo( albumId ).discNumber ); track->setYear( album->getInfo( albumId ).year ); track->setAlbumPtr( albumPtr ); // debug() << " parent album with"<discNumber()<year(); album->addTrack( trackPtr ); } // debug() << "Adding track: " << title << " with id: " << trackId; d->collection->acquireWriteLock(); d->collection->addTrack( trackPtr ); d->collection->releaseLock(); } if( !d->trackResults.contains( trackPtr ) ) d->trackResults.push_back( trackPtr ); } if( !d->expectedReplies.deref() ) { Q_EMIT newTracksReady( d->trackResults ); Q_EMIT queryDone(); d->trackResults.clear(); } } QueryMaker * AmpacheServiceQueryMaker::addFilter( qint64 value, const QString & filter, bool matchBegin, bool matchEnd ) { Q_UNUSED( matchBegin ) Q_UNUSED( matchEnd ) //for now, only accept artist filters // TODO: What about albumArtist? if( value == Meta::valArtist ) { d->artistFilter = filter; } else if( value == Meta::valAlbum ) { d->albumFilter = filter; } else { warning() << "unsupported filter" << Meta::nameForField( value ); } return this; } QueryMaker* AmpacheServiceQueryMaker::addNumberFilter( qint64 value, qint64 filter, QueryMaker::NumberComparison compare ) { if( value == Meta::valCreateDate && compare == QueryMaker::GreaterThan ) { debug() << "asking to filter based on added date"; d->dateFilter = filter; debug() << "setting dateFilter to:" << d->dateFilter; } else { warning() << "unsupported filter" << Meta::nameForField( value ); } return this; } int AmpacheServiceQueryMaker::validFilterMask() { //we only support artist and album filters for now... return ArtistFilter | AlbumFilter; } QueryMaker * AmpacheServiceQueryMaker::limitMaxResultSize( int size ) { d->maxsize = size; return this; } QUrl AmpacheServiceQueryMaker::getRequestUrl( const QString &action ) const { QUrl url = d->server; QString scheme = url.scheme(); if( scheme != "http" && scheme != "https" ) url.setScheme( "http" ); QUrlQuery query( url ); url = url.adjusted( QUrl::StripTrailingSlash ); url.setPath( url.path() + "/server/xml.server.php" ); query.addQueryItem( "auth", d->sessionId ); if( !action.isEmpty() ) query.addQueryItem( "action", action ); if( d->dateFilter > 0 ) { QDateTime from; from.setTime_t( d->dateFilter ); query.addQueryItem( "add", from.toString( Qt::ISODate ) ); } query.addQueryItem( "limit", QString::number( d->maxsize ) ); url.setQuery( query ); return url; } diff --git a/src/services/ampache/LastfmInfoParser.cpp b/src/services/ampache/LastfmInfoParser.cpp index 8c425f3ed9..708a26cd9f 100644 --- a/src/services/ampache/LastfmInfoParser.cpp +++ b/src/services/ampache/LastfmInfoParser.cpp @@ -1,185 +1,185 @@ /*************************************************************************************** * Copyright (c) 2009 Dan Meltzer * * * * 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, see . * ****************************************************************************************/ #include "LastfmInfoParser.h" #include "core/meta/Meta.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include #include #include void LastfmInfoParser::getInfo(const Meta::TrackPtr &track) { DEBUG_BLOCK QMap query; query[ "method" ] = "track.getInfo"; query[ "track" ] = track->name(); query[ "album" ] = track->album() ? track->album()->name() : QString(); query[ "artist" ] = track->artist() ? track->artist()->name() : QString(); query[ "apikey" ] = Amarok::lastfmApiKey(); m_jobs[ "getTrackInfo" ] = lastfm::ws::post( query ); connect( m_jobs[ "getTrackInfo" ], &QNetworkReply::finished, this, &LastfmInfoParser::onGetTrackInfo ); } void LastfmInfoParser::onGetTrackInfo() { DEBUG_BLOCK if( !m_jobs[ "getTrackInfo" ] ) { debug() << "WARNING: GOT RESULT but no object"; return; } switch ( m_jobs[ "getTrackInfo" ]->error() ) { case QNetworkReply::NoError: { lastfm::XmlQuery lfm; lfm.parse( m_jobs[ "getTrackInfo" ]->readAll() ); lastfm::XmlQuery wiki = lfm["track"]["wiki"]; const QString contentText = wiki["content"].text(); const QString publishedDate = wiki["published"].text(); QString html; if( !contentText.isEmpty() ) - html = QString("

%1

Updated: %2

").arg( contentText, publishedDate ); + html = QStringLiteral("

%2

%1

").arg( i18n("Updated: %1", publishedDate), contentText ); else html = i18n( "

No information found for this track.

" ); emit info( html ); break; } default: break; } m_jobs["getTrackInfo"]->deleteLater(); m_jobs["getTrackInfo"] = nullptr; } void LastfmInfoParser::getInfo(const Meta::AlbumPtr &album) { DEBUG_BLOCK QMap query; query[ "method" ] = "album.getInfo"; query[ "album" ] = album->name(); query[ "artist" ] = album->albumArtist() ? album->albumArtist()->name() : QString(); query[ "apikey" ] = Amarok::lastfmApiKey(); m_jobs[ "getAlbumInfo" ] = lastfm::ws::post( query ); connect( m_jobs[ "getAlbumInfo" ], &QNetworkReply::finished, this, &LastfmInfoParser::onGetAlbumInfo ); } void LastfmInfoParser::onGetAlbumInfo() { DEBUG_BLOCK if( !m_jobs[ "getAlbumInfo" ] ) { debug() << "WARNING: GOT RESULT but no object"; return; } switch ( m_jobs[ "getAlbumInfo" ]->error() ) { case QNetworkReply::NoError: { lastfm::XmlQuery lfm; lfm.parse( m_jobs[ "getAlbumInfo" ]->readAll() ); lastfm::XmlQuery wiki = lfm["album"]["wiki"]; const QString summaryText = wiki["summary"].text(); const QString contentText = wiki["content"].text(); const QString publishedDate = wiki["published"].text(); const QString albumUrl = lfm["image size=large"].text(); QString html; if( !contentText.isEmpty() ) html = QString("

%2

Updated: %3

").arg( albumUrl, contentText, publishedDate ); else html = i18n( "

No information found for this album.

" ); emit info( html ); break; } default: break; } m_jobs["getAlbumInfo"]->deleteLater(); m_jobs["getAlbumInfo"] = nullptr; } void LastfmInfoParser::getInfo(const Meta::ArtistPtr &artist) { QMap query; query[ "method" ] = "artist.getInfo"; query[ "artist" ] = artist->name(); debug() << "api key is: " << Amarok::lastfmApiKey(); query[ "apikey" ] = Amarok::lastfmApiKey(); m_jobs[ "getArtistInfo" ] = lastfm::ws::post( query ); connect( m_jobs[ "getArtistInfo" ], &QNetworkReply::finished, this, &LastfmInfoParser::onGetArtistInfo ); } void LastfmInfoParser::onGetArtistInfo() { DEBUG_BLOCK if( !m_jobs[ "getArtistInfo" ] ) { debug() << "WARNING: GOT RESULT but no object"; return; } switch ( m_jobs[ "getArtistInfo" ]->error() ) { case QNetworkReply::NoError: { lastfm::XmlQuery lfm; lfm.parse( m_jobs[ "getArtistInfo" ]->readAll() ); debug() << lfm.text(); lastfm::XmlQuery bio = lfm["artist"]["bio"]; const QString summaryText = bio["summary"].text(); const QString contentText = bio["content"].text(); const QString publishedDate = bio["published"].text(); const QString imageUrl = lfm["image size=large"].text(); QString html; if( !contentText.isEmpty() ) html = QString("

%2

Updated: %3

").arg( imageUrl, contentText, publishedDate ); else html = i18n( "

No information found for this artist.

" ); emit info( html ); break; } default: break; } m_jobs["getArtistInfo"]->deleteLater(); m_jobs["getArtistInfo"] = nullptr; }