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:
parent
b540b9b1f6
commit
4ab5a7d176
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -11,7 +11,6 @@ add_definitions( -DQT_SHARED )
|
||||
add_definitions( -DDLLEXPORT_PRO )
|
||||
|
||||
set( libSources
|
||||
<<<<<<< HEAD
|
||||
tomahawksettings.cpp
|
||||
sourcelist.cpp
|
||||
pipeline.cpp
|
||||
|
@ -16,12 +16,6 @@
|
||||
|
||||
#include "dynamic/GeneratorInterface.h"
|
||||
|
||||
// lame
|
||||
Tomahawk::GeneratorInterface::GeneratorInterface()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Tomahawk::GeneratorInterface::GeneratorInterface( QObject* parent ): QObject( parent )
|
||||
{
|
||||
|
||||
|
@ -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; }
|
||||
|
@ -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 ) );
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user