1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-06-02 14:05:06 +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 <QComboBox>
#include <QPushButton>
#include <QSpinBox>
#include "DynamicControlList.h"
#include "playlistview.h"
@ -27,7 +28,8 @@
#include "trackproxymodel.h"
#include "dynamic/GeneratorInterface.h"
#include "dynamic/GeneratorFactory.h"
#include <QSpinBox>
#include "pipeline.h"
#include <audioengine.h>
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<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->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<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->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<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
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 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;

View File

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

View File

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

View File

@ -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; }

View File

@ -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<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() ) );
}
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<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
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 ) );
}

View File

@ -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;
};