diff --git a/src/dynamic/widgets/DynamicWidget.cpp b/src/dynamic/widgets/DynamicWidget.cpp index afb230bcd..2ba5cc7be 100644 --- a/src/dynamic/widgets/DynamicWidget.cpp +++ b/src/dynamic/widgets/DynamicWidget.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "DynamicControlList.h" #include "playlistview.h" @@ -27,7 +28,8 @@ #include "trackproxymodel.h" #include "dynamic/GeneratorInterface.h" #include "dynamic/GeneratorFactory.h" -#include +#include "pipeline.h" +#include using namespace Tomahawk; @@ -35,6 +37,8 @@ DynamicWidget::DynamicWidget( const Tomahawk::dynplaylist_ptr& playlist, QWidget : QWidget(parent) , m_layout( new QVBoxLayout ) , m_resolveOnNextLoad( false ) + , m_runningOnDemand( false ) + , m_songsSinceLastResolved( 0 ) , m_headerText( 0 ) , m_headerLayout( 0 ) , m_modeCombo( 0 ) @@ -113,6 +117,7 @@ void DynamicWidget::loadDynamicPlaylist(const Tomahawk::dynplaylist_ptr& playlis if( !m_playlist.isNull() ) { disconnect( m_playlist->generator().data(), SIGNAL( generated( QList ) ), this, SLOT( tracksGenerated( QList ) ) ); disconnect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ), this, SLOT(onRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ) ); + disconnect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( onDemandFetched( Tomahawk::query_ptr ) ) ); } m_playlist = playlist; @@ -125,6 +130,7 @@ void DynamicWidget::loadDynamicPlaylist(const Tomahawk::dynplaylist_ptr& playlis applyModeChange( m_playlist->mode() ); connect( m_playlist->generator().data(), SIGNAL( generated( QList ) ), this, SLOT( tracksGenerated( QList ) ) ); connect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) ); + connect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( onDemandFetched( Tomahawk::query_ptr ) ) ); } @@ -154,6 +160,13 @@ DynamicWidget::generateOrStart() { // get the items from the generator, and put them in the playlist m_playlist->generator()->generate( m_genNumber->value() ); + } else if( m_playlist->mode() == OnDemand ) { + if( m_runningOnDemand == false ) { + m_runningOnDemand = true; + m_playlist->generator()->startOnDemand(); + } else { // stop + m_runningOnDemand = false; + } } } @@ -167,16 +180,21 @@ DynamicWidget::modeChanged( int mode ) m_playlist->createNewRevision(); } -void DynamicWidget::applyModeChange( int mode ) +void +DynamicWidget::applyModeChange( int mode ) { if( mode == OnDemand ) { m_generateButton->setText( tr( "Play" ) ); m_genNumber->hide(); + + connect( TomahawkApp::instance()->audioEngine(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) ); } else if( mode == Static ) { m_generateButton->setText( tr( "Generate" ) ); m_genNumber->show(); m_headerLayout->insertWidget( 4, m_genNumber ); + + disconnect( TomahawkApp::instance()->audioEngine(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) ); } } @@ -188,13 +206,49 @@ DynamicWidget::tracksGenerated( const QList< query_ptr >& queries ) m_resolveOnNextLoad = true; } -void DynamicWidget::controlsChanged() +void +DynamicWidget::onDemandFetched( const Tomahawk::query_ptr& track ) +{ + connect( track.data(), SIGNAL( resolveFailed() ), this, SLOT( trackResolveFailed() ) ); + connect( track.data(), SIGNAL( resultsAdded( QList ) ), this, SLOT( trackResolved() ) ); + + m_model->appendTrack( track ); + Pipeline::instance()->add( track ); +} + +void +DynamicWidget::trackResolved() +{ + m_songsSinceLastResolved = 0; +} + +void +DynamicWidget::trackResolveFailed() +{ + m_songsSinceLastResolved++; + if( m_songsSinceLastResolved < 100 ) { + m_playlist->generator()->fetchNext(); + } +} + +void +DynamicWidget::newTrackLoading() +{ + if( m_runningOnDemand && m_songsSinceLastResolved == 0 ) { // if we're in dynamic mode and we're also currently idle + m_playlist->generator()->fetchNext(); + } +} + + +void +DynamicWidget::controlsChanged() { // save the current playlist m_playlist->createNewRevision(); } -void DynamicWidget::controlChanged(const Tomahawk::dyncontrol_ptr& control) +void +DynamicWidget::controlChanged(const Tomahawk::dyncontrol_ptr& control) { } diff --git a/src/dynamic/widgets/DynamicWidget.h b/src/dynamic/widgets/DynamicWidget.h index 2c73978ee..f81af58a3 100644 --- a/src/dynamic/widgets/DynamicWidget.h +++ b/src/dynamic/widgets/DynamicWidget.h @@ -61,6 +61,12 @@ private slots: void modeChanged(int); void tracksGenerated( const QList< Tomahawk::query_ptr>& queries ); + // used by on demand mode + void newTrackLoading(); + void onDemandFetched( const Tomahawk::query_ptr& track ); + void trackResolveFailed(); + void trackResolved(); + void controlsChanged(); void controlChanged( const Tomahawk::dyncontrol_ptr& control ); @@ -72,6 +78,7 @@ private: bool m_resolveOnNextLoad; // used in OnDemand mode + bool m_runningOnDemand; int m_songsSinceLastResolved; QLabel* m_headerText; diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index e5dc34d50..d4cbf8da8 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -11,7 +11,6 @@ add_definitions( -DQT_SHARED ) add_definitions( -DDLLEXPORT_PRO ) set( libSources -<<<<<<< HEAD tomahawksettings.cpp sourcelist.cpp pipeline.cpp diff --git a/src/libtomahawk/dynamic/GeneratorInterface.cpp b/src/libtomahawk/dynamic/GeneratorInterface.cpp index 194d935bf..6710c3af5 100644 --- a/src/libtomahawk/dynamic/GeneratorInterface.cpp +++ b/src/libtomahawk/dynamic/GeneratorInterface.cpp @@ -16,12 +16,6 @@ #include "dynamic/GeneratorInterface.h" -// lame -Tomahawk::GeneratorInterface::GeneratorInterface() -{ - -} - Tomahawk::GeneratorInterface::GeneratorInterface( QObject* parent ): QObject( parent ) { diff --git a/src/libtomahawk/dynamic/GeneratorInterface.h b/src/libtomahawk/dynamic/GeneratorInterface.h index 0b5665cab..f27093203 100644 --- a/src/libtomahawk/dynamic/GeneratorInterface.h +++ b/src/libtomahawk/dynamic/GeneratorInterface.h @@ -46,7 +46,6 @@ class GeneratorInterface : public QObject public: // can't inline constructors/destructors for forward declared shared pointer types - GeneratorInterface(); explicit GeneratorInterface( QObject* parent = 0 ); virtual ~GeneratorInterface(); @@ -75,6 +74,11 @@ public: */ virtual void startOnDemand() {} + /** + * Get the next on demand track. + * \param rating Rating from 1-5, -1 for none + */ + virtual void fetchNext( int rating = -1 ) {} /// The type of this generator QString type() const { return m_type; } diff --git a/src/libtomahawk/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/dynamic/echonest/EchonestGenerator.cpp index 3b3a43f2f..1d4b0143f 100644 --- a/src/libtomahawk/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/dynamic/echonest/EchonestGenerator.cpp @@ -45,9 +45,9 @@ EchonestFactory::typeSelectors() const << "Longitude" << "Latitude" << "Mode" << "Key" << "Sorting"; } - EchonestGenerator::EchonestGenerator ( QObject* parent ) : GeneratorInterface ( parent ) + , m_dynPlaylist( new Echonest::DynamicPlaylist() ) { m_type = "echonest"; m_mode = OnDemand; @@ -57,7 +57,7 @@ EchonestGenerator::EchonestGenerator ( QObject* parent ) EchonestGenerator::~EchonestGenerator() { - + delete m_dynPlaylist; } dyncontrol_ptr @@ -77,21 +77,39 @@ void EchonestGenerator::generate ( int number ) { // convert to an echonest query, and fire it off - if( number < 0 ) { // dynamic - - } else { // static - Echonest::DynamicPlaylist::PlaylistParams params; - foreach( const dyncontrol_ptr& control, m_controls ) { - params.append( control.dynamicCast()->toENParam() ); - } - params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, determineRadioType() ) ); - params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Results, number ) ); - QNetworkReply* reply = Echonest::DynamicPlaylist::staticPlaylist( params ); - qDebug() << "Generating a static playlist from echonest!" << reply->url().toString(); - connect( reply, SIGNAL( finished() ), this, SLOT( staticFinished() ) ); - } + Echonest::DynamicPlaylist::PlaylistParams params = getParams(); + params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Results, number ) ); + QNetworkReply* reply = Echonest::DynamicPlaylist::staticPlaylist( params ); + qDebug() << "Generating a static playlist from echonest!" << reply->url().toString(); + connect( reply, SIGNAL( finished() ), this, SLOT( staticFinished() ) ); + } +void +EchonestGenerator::startOnDemand() +{ + Echonest::DynamicPlaylist::PlaylistParams params = getParams(); + + QNetworkReply* reply = m_dynPlaylist->start( params ); + qDebug() << "starting a dynamic playlist from echonest!" << reply->url().toString(); + connect( reply, SIGNAL( finished() ), this, SLOT( dynamicStarted() ) ); +} + +void +EchonestGenerator::fetchNext( int rating ) +{ + if( m_dynPlaylist->sessionId().isEmpty() ) { + // we're not currently playing, oops! + qWarning() << Q_FUNC_INFO << "asked to fetch next dynamic song when we're not in the middle of a playlist!"; + return; + } + + QNetworkReply* reply = m_dynPlaylist->fetchNextSong( rating ); + qDebug() << "getting next song from echonest" << reply->url().toString(); + connect( reply, SIGNAL( finished() ), this, SLOT( dynamicFetched() ) ); +} + + void EchonestGenerator::staticFinished() { @@ -112,21 +130,71 @@ EchonestGenerator::staticFinished() QList< query_ptr > queries; foreach( const Echonest::Song& song, songs ) { qDebug() << "EchonestGenerator got song:" << song; - QVariantMap track; - track[ "artist" ] = song.artistName(); -// track[ "album" ] = song.release(); // TODO should we include it? can be quite specific - track[ "track" ] = song.title(); - queries << query_ptr( new Query( track ) ); + queries << queryFromSong( song ); } emit generated( queries ); } +Echonest::DynamicPlaylist::PlaylistParams EchonestGenerator::getParams() const +{ + Echonest::DynamicPlaylist::PlaylistParams params; + foreach( const dyncontrol_ptr& control, m_controls ) { + params.append( control.dynamicCast()->toENParam() ); + } + params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, determineRadioType() ) ); + return params; +} + +void +EchonestGenerator::dynamicStarted() +{ + Q_ASSERT( sender() ); + Q_ASSERT( qobject_cast< QNetworkReply* >( sender() ) ); + QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() ); + + try + { + Echonest::Song song = m_dynPlaylist->parseStart( reply ); + query_ptr songQuery = queryFromSong( song ); + emit nextTrackGenerated( songQuery ); + } catch( const Echonest::ParseError& e ) { + qWarning() << "libechonest threw an error parsing the start of the dynamic playlist:" << e.errorType() << e.what(); + } +} + +void +EchonestGenerator::dynamicFetched() +{ + Q_ASSERT( sender() ); + Q_ASSERT( qobject_cast< QNetworkReply* >( sender() ) ); + QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() ); + + try + { + Echonest::Song song = m_dynPlaylist->parseNextSong( reply ); + query_ptr songQuery = queryFromSong( song ); + emit nextTrackGenerated( songQuery ); + } catch( const Echonest::ParseError& e ) { + qWarning() << "libechonest threw an error parsing the next song of the dynamic playlist:" << e.errorType() << e.what(); + } +} + // tries to heuristically determine what sort of radio this is based on the controls -Echonest::DynamicPlaylist::ArtistTypeEnum EchonestGenerator::determineRadioType() const +Echonest::DynamicPlaylist::ArtistTypeEnum +EchonestGenerator::determineRadioType() const { // TODO return Echonest::DynamicPlaylist::ArtistRadioType; } +query_ptr +EchonestGenerator::queryFromSong(const Echonest::Song& song) +{ + QVariantMap track; + track[ "artist" ] = song.artistName(); + // track[ "album" ] = song.release(); // TODO should we include it? can be quite specific + track[ "track" ] = song.title(); + return query_ptr( new Query( track ) ); +} diff --git a/src/libtomahawk/dynamic/echonest/EchonestGenerator.h b/src/libtomahawk/dynamic/echonest/EchonestGenerator.h index ac1402956..a11e50652 100644 --- a/src/libtomahawk/dynamic/echonest/EchonestGenerator.h +++ b/src/libtomahawk/dynamic/echonest/EchonestGenerator.h @@ -46,12 +46,20 @@ public: virtual dyncontrol_ptr createControl( const QString& type = QString() ); virtual QPixmap logo(); virtual void generate ( int number = -1 ); + virtual void startOnDemand(); + virtual void fetchNext( int rating = -1 ); private slots: void staticFinished(); + void dynamicStarted(); + void dynamicFetched(); private: + Echonest::DynamicPlaylist::PlaylistParams getParams() const; + query_ptr queryFromSong( const Echonest::Song& song ); + Echonest::DynamicPlaylist::ArtistTypeEnum determineRadioType() const; + Echonest::DynamicPlaylist* m_dynPlaylist; QPixmap m_logo; };