1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-07-31 03:10:12 +02:00

Add support for incremental fetching for generators

Add logic in DynamicWidget to handle on-demand playists
This commit is contained in:
Leo Franchi
2011-01-14 16:02:25 -05:00
parent b540b9b1f6
commit 4ab5a7d176
7 changed files with 167 additions and 33 deletions

View File

@@ -20,6 +20,7 @@
#include <QLabel> #include <QLabel>
#include <QComboBox> #include <QComboBox>
#include <QPushButton> #include <QPushButton>
#include <QSpinBox>
#include "DynamicControlList.h" #include "DynamicControlList.h"
#include "playlistview.h" #include "playlistview.h"
@@ -27,7 +28,8 @@
#include "trackproxymodel.h" #include "trackproxymodel.h"
#include "dynamic/GeneratorInterface.h" #include "dynamic/GeneratorInterface.h"
#include "dynamic/GeneratorFactory.h" #include "dynamic/GeneratorFactory.h"
#include <QSpinBox> #include "pipeline.h"
#include <audioengine.h>
using namespace Tomahawk; using namespace Tomahawk;
@@ -35,6 +37,8 @@ DynamicWidget::DynamicWidget( const Tomahawk::dynplaylist_ptr& playlist, QWidget
: QWidget(parent) : QWidget(parent)
, m_layout( new QVBoxLayout ) , m_layout( new QVBoxLayout )
, m_resolveOnNextLoad( false ) , m_resolveOnNextLoad( false )
, m_runningOnDemand( false )
, m_songsSinceLastResolved( 0 )
, m_headerText( 0 ) , m_headerText( 0 )
, m_headerLayout( 0 ) , m_headerLayout( 0 )
, m_modeCombo( 0 ) , m_modeCombo( 0 )
@@ -113,6 +117,7 @@ void DynamicWidget::loadDynamicPlaylist(const Tomahawk::dynplaylist_ptr& playlis
if( !m_playlist.isNull() ) { if( !m_playlist.isNull() ) {
disconnect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) ); disconnect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
disconnect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ), this, SLOT(onRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ) ); 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; m_playlist = playlist;
@@ -125,6 +130,7 @@ void DynamicWidget::loadDynamicPlaylist(const Tomahawk::dynplaylist_ptr& playlis
applyModeChange( m_playlist->mode() ); applyModeChange( m_playlist->mode() );
connect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) ); connect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
connect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) ); 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 // get the items from the generator, and put them in the playlist
m_playlist->generator()->generate( m_genNumber->value() ); 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(); m_playlist->createNewRevision();
} }
void DynamicWidget::applyModeChange( int mode ) void
DynamicWidget::applyModeChange( int mode )
{ {
if( mode == OnDemand ) if( mode == OnDemand )
{ {
m_generateButton->setText( tr( "Play" ) ); m_generateButton->setText( tr( "Play" ) );
m_genNumber->hide(); m_genNumber->hide();
connect( TomahawkApp::instance()->audioEngine(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) );
} else if( mode == Static ) { } else if( mode == Static ) {
m_generateButton->setText( tr( "Generate" ) ); m_generateButton->setText( tr( "Generate" ) );
m_genNumber->show(); m_genNumber->show();
m_headerLayout->insertWidget( 4, m_genNumber ); 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; 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<Tomahawk::result_ptr> ) ), 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 // save the current playlist
m_playlist->createNewRevision(); m_playlist->createNewRevision();
} }
void DynamicWidget::controlChanged(const Tomahawk::dyncontrol_ptr& control) void
DynamicWidget::controlChanged(const Tomahawk::dyncontrol_ptr& control)
{ {
} }

View File

@@ -61,6 +61,12 @@ private slots:
void modeChanged(int); void modeChanged(int);
void tracksGenerated( const QList< Tomahawk::query_ptr>& queries ); 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 controlsChanged();
void controlChanged( const Tomahawk::dyncontrol_ptr& control ); void controlChanged( const Tomahawk::dyncontrol_ptr& control );
@@ -72,6 +78,7 @@ private:
bool m_resolveOnNextLoad; bool m_resolveOnNextLoad;
// used in OnDemand mode // used in OnDemand mode
bool m_runningOnDemand;
int m_songsSinceLastResolved; int m_songsSinceLastResolved;
QLabel* m_headerText; QLabel* m_headerText;

View File

@@ -11,7 +11,6 @@ add_definitions( -DQT_SHARED )
add_definitions( -DDLLEXPORT_PRO ) add_definitions( -DDLLEXPORT_PRO )
set( libSources set( libSources
<<<<<<< HEAD
tomahawksettings.cpp tomahawksettings.cpp
sourcelist.cpp sourcelist.cpp
pipeline.cpp pipeline.cpp

View File

@@ -16,12 +16,6 @@
#include "dynamic/GeneratorInterface.h" #include "dynamic/GeneratorInterface.h"
// lame
Tomahawk::GeneratorInterface::GeneratorInterface()
{
}
Tomahawk::GeneratorInterface::GeneratorInterface( QObject* parent ): QObject( parent ) Tomahawk::GeneratorInterface::GeneratorInterface( QObject* parent ): QObject( parent )
{ {

View File

@@ -46,7 +46,6 @@ class GeneratorInterface : public QObject
public: public:
// can't inline constructors/destructors for forward declared shared pointer types // can't inline constructors/destructors for forward declared shared pointer types
GeneratorInterface();
explicit GeneratorInterface( QObject* parent = 0 ); explicit GeneratorInterface( QObject* parent = 0 );
virtual ~GeneratorInterface(); virtual ~GeneratorInterface();
@@ -75,6 +74,11 @@ public:
*/ */
virtual void startOnDemand() {} 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 /// The type of this generator
QString type() const { return m_type; } QString type() const { return m_type; }

View File

@@ -45,9 +45,9 @@ EchonestFactory::typeSelectors() const
<< "Longitude" << "Latitude" << "Mode" << "Key" << "Sorting"; << "Longitude" << "Latitude" << "Mode" << "Key" << "Sorting";
} }
EchonestGenerator::EchonestGenerator ( QObject* parent ) EchonestGenerator::EchonestGenerator ( QObject* parent )
: GeneratorInterface ( parent ) : GeneratorInterface ( parent )
, m_dynPlaylist( new Echonest::DynamicPlaylist() )
{ {
m_type = "echonest"; m_type = "echonest";
m_mode = OnDemand; m_mode = OnDemand;
@@ -57,7 +57,7 @@ EchonestGenerator::EchonestGenerator ( QObject* parent )
EchonestGenerator::~EchonestGenerator() EchonestGenerator::~EchonestGenerator()
{ {
delete m_dynPlaylist;
} }
dyncontrol_ptr dyncontrol_ptr
@@ -77,21 +77,39 @@ void
EchonestGenerator::generate ( int number ) EchonestGenerator::generate ( int number )
{ {
// convert to an echonest query, and fire it off // convert to an echonest query, and fire it off
if( number < 0 ) { // dynamic Echonest::DynamicPlaylist::PlaylistParams params = getParams();
params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Results, number ) );
} else { // static QNetworkReply* reply = Echonest::DynamicPlaylist::staticPlaylist( params );
Echonest::DynamicPlaylist::PlaylistParams params; qDebug() << "Generating a static playlist from echonest!" << reply->url().toString();
foreach( const dyncontrol_ptr& control, m_controls ) { connect( reply, SIGNAL( finished() ), this, SLOT( staticFinished() ) );
params.append( control.dynamicCast<EchonestControl>()->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() ) );
}
} }
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 void
EchonestGenerator::staticFinished() EchonestGenerator::staticFinished()
{ {
@@ -112,21 +130,71 @@ EchonestGenerator::staticFinished()
QList< query_ptr > queries; QList< query_ptr > queries;
foreach( const Echonest::Song& song, songs ) { foreach( const Echonest::Song& song, songs ) {
qDebug() << "EchonestGenerator got song:" << song; qDebug() << "EchonestGenerator got song:" << song;
QVariantMap track; queries << queryFromSong( song );
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 ) );
} }
emit generated( queries ); emit generated( queries );
} }
Echonest::DynamicPlaylist::PlaylistParams EchonestGenerator::getParams() const
{
Echonest::DynamicPlaylist::PlaylistParams params;
foreach( const dyncontrol_ptr& control, m_controls ) {
params.append( control.dynamicCast<EchonestControl>()->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 // 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 // TODO
return Echonest::DynamicPlaylist::ArtistRadioType; 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 ) );
}

View File

@@ -46,12 +46,20 @@ public:
virtual dyncontrol_ptr createControl( const QString& type = QString() ); virtual dyncontrol_ptr createControl( const QString& type = QString() );
virtual QPixmap logo(); virtual QPixmap logo();
virtual void generate ( int number = -1 ); virtual void generate ( int number = -1 );
virtual void startOnDemand();
virtual void fetchNext( int rating = -1 );
private slots: private slots:
void staticFinished(); void staticFinished();
void dynamicStarted();
void dynamicFetched();
private: private:
Echonest::DynamicPlaylist::PlaylistParams getParams() const;
query_ptr queryFromSong( const Echonest::Song& song );
Echonest::DynamicPlaylist::ArtistTypeEnum determineRadioType() const; Echonest::DynamicPlaylist::ArtistTypeEnum determineRadioType() const;
Echonest::DynamicPlaylist* m_dynPlaylist;
QPixmap m_logo; QPixmap m_logo;
}; };