diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 6d64c0d0b..515866eb4 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -110,6 +110,7 @@ set( libSources infosystem/infosystemworker.cpp infosystem/infoplugins/generic/echonestplugin.cpp infosystem/infoplugins/generic/lastfmplugin.cpp + infosystem/infoplugins/generic/chartsplugin.cpp infosystem/infoplugins/generic/musixmatchplugin.cpp infosystem/infoplugins/generic/musicbrainzPlugin.cpp @@ -335,6 +336,7 @@ set( libHeaders infosystem/infosystemcache.h infosystem/infoplugins/generic/echonestplugin.h infosystem/infoplugins/generic/lastfmplugin.h + infosystem/infoplugins/generic/chartsplugin.h infosystem/infoplugins/generic/musixmatchplugin.h infosystem/infoplugins/generic/musicbrainzPlugin.h diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp new file mode 100644 index 000000000..e75ddf95b --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp @@ -0,0 +1,403 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Hugo Lindström + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "chartsplugin.h" + +#include +#include +#include +#include +#include + +#include "album.h" +#include "typedefs.h" +#include "audio/audioengine.h" +#include "tomahawksettings.h" +#include "utils/tomahawkutils.h" +#include "utils/logger.h" + +#include + +#define CHART_URL "http://charts.tomahawk-player.org:10080/" +#include + +using namespace Tomahawk::InfoSystem; + +static QString +md5( const QByteArray& src ) +{ + QByteArray const digest = QCryptographicHash::hash( src, QCryptographicHash::Md5 ); + return QString::fromLatin1( digest.toHex() ).rightJustified( 32, '0' ); +} + + +ChartsPlugin::ChartsPlugin() + : InfoPlugin() +{ + + tDebug() << "ChartsPlugin: InfoChart fetching possible resources"; + + QUrl url = QUrl(CHART_URL); + QNetworkReply* reply = lastfm::nam()->get( QNetworkRequest( url ) ); + connect( reply, SIGNAL( finished() ), SLOT( chartResources() ) ); + + m_supportedGetTypes << InfoChart << InfoChartCapabilities; + +} + + +ChartsPlugin::~ChartsPlugin() +{ + qDebug() << Q_FUNC_INFO; +} + + +void +ChartsPlugin::namChangedSlot( QNetworkAccessManager *nam ) +{ + qDebug() << Q_FUNC_INFO; + if( !nam ) + return; +} + + +void +ChartsPlugin::dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + emit info( requestId, requestData, QVariant() ); + return; +} + + +void +ChartsPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + switch ( requestData.type ) + { + + case InfoChart: + fetchChart( requestId, requestData ); + break; + + case InfoChartCapabilities: + fetchChartCapabilities( requestId, requestData ); + break; + default: + dataError( requestId, requestData ); + } +} + + +void +ChartsPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input ) +{ + Q_UNUSED( caller ) + Q_UNUSED( type) + Q_UNUSED( input ) +} + + + + +void +ChartsPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoCriteriaHash >() ) + { + dataError( requestId, requestData ); + return; + } + InfoCriteriaHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash >(); + Tomahawk::InfoSystem::InfoCriteriaHash criteria; + if ( !hash.contains( "chart_id" ) ) + { + dataError( requestId, requestData ); + return; + } else { + criteria["chart_id"] = hash["chart_id"]; + } + if ( hash.contains( "chart_source" ) ) + { + criteria["chart_source"] = hash["chart_source"]; + } + + emit getCachedInfo( requestId, criteria, 0, requestData ); +} + +void +ChartsPlugin::fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoCriteriaHash >() ) + { + dataError( requestId, requestData ); + return; + } + InfoCriteriaHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash >(); + Tomahawk::InfoSystem::InfoCriteriaHash criteria; + + emit getCachedInfo( requestId, criteria, 0, requestData ); +} + +void +ChartsPlugin::notInCacheSlot( uint requestId, QHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !lastfm::nam() ) + { + tLog() << "Have a null QNAM, uh oh"; + emit info( requestId, requestData, QVariant() ); + return; + } + + switch ( requestData.type ) + { + case InfoChart: + { + + QUrl url = QUrl( QString( CHART_URL "/source/%1/chart/%2" ).arg( criteria["chart_source"] ).arg( criteria["chart_id"] ) ); + qDebug() << Q_FUNC_INFO << "Getting chart url" << url; + + QNetworkReply* reply = lastfm::nam()->get( QNetworkRequest( url ) ); + reply->setProperty( "requestId", requestId ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); + + connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) ); + return; + } + + case InfoChartCapabilities: + { + + QVariantMap result; + foreach( QVariant chartResource, m_chartResources ) + { + + QList album_charts; + QList track_charts; + QVariantMap charts; + + if( !m_chartTypes.isEmpty() ) + foreach( QVariant type, m_chartTypes ) + { + // Itunes supplys charts based on geo, for now, only take US charts + if( type.toMap().value( "source" ).toString() == chartResource.toString() + && type.toMap().value( "geo" ).isValid() + && type.toMap().value( "geo" ).toString() != "us" ) + continue; + + if( type.toMap().value( "source" ).toString() == chartResource.toString() ) + { + if( type.toMap().value( "type" ).toString() == "Album" ) + { + album_charts.append( Chart( type.toMap().value("id").toString(), type.toMap().value("name").toString(), "album" ) ); + charts.insert( "Albums", QVariant::fromValue >( album_charts ) ); + } + if( type.toMap().value( "type" ).toString() == "Track" ) + { + track_charts.append( Chart( type.toMap().value("id").toString(), type.toMap().value("name").toString(), "tracks" ) ); + charts.insert( "Tracks", QVariant::fromValue >( track_charts ) ); + } + } + } + + result.insert( chartResource.toString() , QVariant::fromValue( charts ) ); + } + emit info( + requestId, + requestData, + result + ); + return; + } + + + + default: + { + tLog() << "Couldn't figure out what to do with this type of request after cache miss"; + emit info( requestId, requestData, QVariant() ); + return; + } + } +} + + +void +ChartsPlugin::chartResources() +{ + + tDebug() << "ChartsPlugin: InfoChart resources returned!"; + QNetworkReply* reply = qobject_cast( sender() ); + + + if ( reply->error() == QNetworkReply::NoError ) + { + QJson::Parser p; + bool ok; + QVariantMap res = p.parse( reply, &ok ).toMap(); + + if ( !ok ) + { + tLog() << "Failed to parse resources" << p.errorString() << "On line" << p.errorLine(); + + return; + } + + m_chartResources = res.value( "chart_sources" ).toList(); + qDebug() << "Resources" << m_chartResources; + + if(!m_chartResources.isEmpty()){ + + foreach(QVariant resource, m_chartResources){ + tDebug() << "ChartsPlugin: InfoChart fetching possible types for "<< resource.toString(); + + QUrl url = QUrl( QString( CHART_URL "/source/%1" ).arg(resource.toString() ) ); + qDebug() << "Getting types from " << url; + + QNetworkReply* reply = lastfm::nam()->get( QNetworkRequest( url ) ); + + connect( reply, SIGNAL( finished() ), SLOT( chartTypes() ) ); + } + } + } + +} + +void +ChartsPlugin::chartTypes() +{ + + tDebug() << "ChartsPlugin: InfoChart types returned!"; + QNetworkReply* reply = qobject_cast( sender() ); + + + if ( reply->error() == QNetworkReply::NoError ) + { + QJson::Parser p; + bool ok; + QVariantMap res = p.parse( reply, &ok ).toMap(); + + if ( !ok ) + { + tLog() << "Failed to parse resources" << p.errorString() << "On line" << p.errorLine(); + + return; + } + + foreach(QVariant chart, res.value( "charts" ).toMap() ) + m_chartTypes.append(chart); + + } + +} + +void +ChartsPlugin::chartReturned() +{ + + QNetworkReply* reply = qobject_cast( sender() ); + + QVariantMap returnedData; + + if ( reply->error() == QNetworkReply::NoError ) + { + QJson::Parser p; + bool ok; + QVariantMap res = p.parse( reply, &ok ).toMap(); + + if ( !ok ) + { + tLog() << "Failed to parse json from chart lookup:" << p.errorString() << "On line" << p.errorLine(); + + return; + }else qDebug() << res; + + + QVariantList chartResponse = res.value( "list" ).toList(); + QList top_tracks; + QList top_albums; + + + if( res.value( "type" ).toString() == "Album" ) + setChartType( Album ); + else if( res.value( "type" ).toString() == "Track" ) + setChartType( Track ); + else + setChartType( None ); + + + foreach ( QVariant chartR, chartResponse ) + { + QString title, artist, album; + QVariantMap chartMap = chartR.toMap(); + + if ( chartMap.contains( "track" ) ) + { + + title = chartMap.value( "track" ).toString(); + artist = chartMap.value( "artist" ).toString(); + + if ( title.isEmpty() && artist.isEmpty() ) // don't have enough... + { + tLog() << "Didn't get an artist and track name from itunes, not enough to build a query on. Aborting" << title << artist << album; + + } + else{ + + if( chartType() == Album ){ + + ArtistAlbumPair pair; + pair.artist = artist; + pair.album = title; + top_albums << pair; + + }else if( chartType() == Track ){ + + ArtistTrackPair pair; + pair.artist = artist; + pair.track = title; + top_tracks << pair; + } + + } + } + } + if( chartType() == Track ){ + tDebug() << "ChartsPlugin:" << "\tgot " << top_tracks.size() << " tracks"; + returnedData["tracks"] = QVariant::fromValue( top_tracks ); + returnedData["type"] = "tracks"; + } + + if( chartType() == Album ){ + tDebug() << "ChartsPlugin:" << "\tgot " << top_albums.size() << " albums"; + returnedData["albums"] = QVariant::fromValue( top_albums ); + returnedData["type"] = "albums"; + } + + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + + emit info( + reply->property( "requestId" ).toUInt(), + requestData, + returnedData + ); + // TODO update cache + + }else qDebug() << "Network error"; + +} + diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h new file mode 100644 index 000000000..b358f3a91 --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h @@ -0,0 +1,81 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Hugo Lindström + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef ChartsPlugin_H +#define ChartsPlugin_H + +#include "infosystem/infosystem.h" +#include "infosystem/infosystemworker.h" + +#include + +class QNetworkReply; + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class ChartsPlugin : public InfoPlugin +{ + Q_OBJECT + +public: + ChartsPlugin(); + virtual ~ChartsPlugin(); + + enum ChartType { + None = 0x00, + Track = 0x01, + Album = 0x02, + Artist = 0x04 + + }; + void setChartType( ChartType type ) { m_chartType = type; } + ChartType chartType() const { return m_chartType; } + +public slots: + void chartReturned(); + void chartResources(); + void chartTypes(); + void namChangedSlot( QNetworkAccessManager *nam ); + +protected slots: + virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoCriteriaHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + + virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ); + +private: + void fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + + QVariantList m_chartResources; + QVariantList m_chartTypes; + QList m_charts; + ChartType m_chartType; + +}; + +} + +} + +#endif // ChartsPlugin_H diff --git a/src/libtomahawk/infosystem/infosystem.h b/src/libtomahawk/infosystem/infosystem.h index 4de22ea6d..23ea52246 100644 --- a/src/libtomahawk/infosystem/infosystem.h +++ b/src/libtomahawk/infosystem/infosystem.h @@ -135,6 +135,11 @@ struct ArtistTrackPair { QString track; }; +struct ArtistAlbumPair { + QString artist; + QString album; +}; + struct Chart { Chart(){} Chart(const QString _id, const QString _label, const QString _type) { @@ -284,8 +289,10 @@ Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoGenericMap ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCriteriaHash ); Q_DECLARE_METATYPE( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache > ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::ArtistTrackPair ); +Q_DECLARE_METATYPE( Tomahawk::InfoSystem::ArtistAlbumPair ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::Chart ); Q_DECLARE_METATYPE( QList ); +Q_DECLARE_METATYPE( QList ); Q_DECLARE_METATYPE( QList ); #endif // TOMAHAWK_INFOSYSTEM_H diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp index b868503cf..0df655dfa 100644 --- a/src/libtomahawk/infosystem/infosystemworker.cpp +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -25,6 +25,7 @@ #include "infosystemcache.h" #include "infoplugins/generic/echonestplugin.h" #include "infoplugins/generic/musixmatchplugin.h" +#include "infoplugins/generic/chartsplugin.h" #include "infoplugins/generic/lastfmplugin.h" #include "infoplugins/generic/musicbrainzPlugin.h" #include "utils/tomahawkutils.h" @@ -85,10 +86,18 @@ InfoSystemWorker::init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache> cac InfoPluginPtr mbptr( new MusicBrainzPlugin() ); m_plugins.append( mbptr ); registerInfoTypes( mbptr, mbptr.data()->supportedGetTypes(), mbptr.data()->supportedPushTypes() ); + + + + InfoPluginPtr chartsptr( new ChartsPlugin() ); + m_plugins.append( chartsptr ); + registerInfoTypes( chartsptr, chartsptr.data()->supportedGetTypes(), chartsptr.data()->supportedPushTypes() ); + InfoPluginPtr lfmptr( new LastFmPlugin() ); m_plugins.append( lfmptr ); registerInfoTypes( lfmptr, lfmptr.data()->supportedGetTypes(), lfmptr.data()->supportedPushTypes() ); + #ifdef Q_WS_MAC InfoPluginPtr admptr( new AdiumPlugin() ); m_plugins.append( admptr ); diff --git a/src/libtomahawk/widgets/whatshotwidget.cpp b/src/libtomahawk/widgets/whatshotwidget.cpp index 54833f90e..9a189b859 100644 --- a/src/libtomahawk/widgets/whatshotwidget.cpp +++ b/src/libtomahawk/widgets/whatshotwidget.cpp @@ -78,7 +78,6 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent ) ui->breadCrumbRight->setModel(m_crumbModelLeft); ui->breadCrumbRight->setUseAnimation(true);*/ - m_tracksModel = new PlaylistModel( ui->tracksViewLeft ); m_tracksModel->setStyle( TrackModel::Short ); @@ -206,6 +205,21 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat foreach ( const QString& artist, artists ) m_artistsModel->addArtists( Artist::get( artist ) ); } + else if( type == "albums" ) + { + + setLeftViewTracks(); + m_tracksModel->clear(); + + const QList albums = returnedData["albums"].value >(); + tDebug( LOGVERBOSE ) << "WhatsHot: got albums! " << albums.size(); + + foreach ( const Tomahawk::InfoSystem::ArtistAlbumPair& album, albums ) + { + query_ptr query = Query::get( album.artist, QString(), album.album, uuid() ); + m_tracksModel->append( query ); + } + } else if( type == "tracks" ) { setLeftViewTracks(); @@ -241,7 +255,9 @@ WhatsHotWidget::infoSystemFinished( QString target ) void WhatsHotWidget::leftCrumbIndexChanged( QModelIndex index ) { - tDebug( LOGVERBOSE ) << "WhatsHot:: left crumb changed" << index.data(); + + + tDebug( LOGVERBOSE ) << "WhatsHot:: left crumb current changed" << index.data(); QStandardItem* item = m_crumbModelLeft->itemFromIndex(index); if( !item ) return; @@ -249,10 +265,20 @@ WhatsHotWidget::leftCrumbIndexChanged( QModelIndex index ) return; + // Get the base eg. source identifier + QList indexes; + while (index.parent().isValid()) + { + indexes.prepend(index); + index = index.parent(); + } + + const QString chartId = item->data().toString(); Tomahawk::InfoSystem::InfoCriteriaHash criteria; criteria.insert("chart_id", chartId); + criteria.insert("chart_source", index.data().toString()); Tomahawk::InfoSystem::InfoRequestData requestData; QVariantMap customData; @@ -262,6 +288,9 @@ WhatsHotWidget::leftCrumbIndexChanged( QModelIndex index ) requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( criteria ); requestData.type = Tomahawk::InfoSystem::InfoChart; + qDebug() << Q_FUNC_INFO << "RequestData custom" << requestData.customData; + qDebug() << Q_FUNC_INFO << "RequestData caller" << requestData.caller; + qDebug() << Q_FUNC_INFO << "RequestData input" << requestData.input; Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); } diff --git a/src/libtomahawk/widgets/whatshotwidget.h b/src/libtomahawk/widgets/whatshotwidget.h index d6d0b6a0f..6da5eb23a 100644 --- a/src/libtomahawk/widgets/whatshotwidget.h +++ b/src/libtomahawk/widgets/whatshotwidget.h @@ -84,6 +84,7 @@ private slots: private: void setLeftViewArtists(); void setLeftViewTracks(); + QStandardItem* parseNode( QStandardItem* parentItem, const QString &label, const QVariant &data ); Ui::WhatsHotWidget *ui; @@ -91,6 +92,7 @@ private: TreeModel* m_artistsModel; TreeProxyModel* m_artistsProxy; QStandardItemModel* m_crumbModelLeft; + TreeModel* m_albumsModel; QTimer* m_timer; }; diff --git a/src/libtomahawk/widgets/whatshotwidget.ui b/src/libtomahawk/widgets/whatshotwidget.ui index 902455cfb..56e09ebc1 100644 --- a/src/libtomahawk/widgets/whatshotwidget.ui +++ b/src/libtomahawk/widgets/whatshotwidget.ui @@ -30,6 +30,30 @@ + + + true + + + + true + + + + 9 + 9 + 839 + 471 + + + + + 320 + 0 + + + + @@ -57,7 +81,7 @@ ArtistView QTreeView -
artistview.h
+
artistview.h
HeaderBreadCrumb diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index 22f36aeb8..4b78abd85 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -407,7 +407,9 @@ TomahawkApp::registerMetaTypes() qRegisterMetaType< DirLister::Mode >("DirLister::Mode"); qRegisterMetaType< Tomahawk::InfoSystem::ArtistTrackPair >("Tomahawk::InfoSystem::ArtistTrackPair"); + qRegisterMetaType< Tomahawk::InfoSystem::ArtistAlbumPair >("Tomahawk::InfoSystem::ArtistAlbumPair"); qRegisterMetaType< QList >("QList"); + qRegisterMetaType< QList >("QList"); qRegisterMetaType< Tomahawk::InfoSystem::Chart>("Tomahawk::InfoSystem::Chart"); qRegisterMetaType< QList >("QList"); qRegisterMetaType< QPersistentModelIndex >( "QPersistentModelIndex" );