mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-04-07 17:42:35 +02:00
rework the station workflow a bit. now, show a preview of 5 tracks all the time, live-updated based on filters. click on a track to start the station going.
comments / criticisms / bugs appreciated
This commit is contained in:
parent
dfc57b156f
commit
e178f00fab
@ -17,6 +17,7 @@
|
||||
#include "playlist/dynamic/DynamicModel.h"
|
||||
#include "GeneratorInterface.h"
|
||||
#include "audio/audioengine.h"
|
||||
#include <pipeline.h>
|
||||
|
||||
using namespace Tomahawk;
|
||||
|
||||
@ -25,7 +26,7 @@ DynamicModel::DynamicModel( QObject* parent )
|
||||
, m_startOnResolved( false )
|
||||
, m_onDemandRunning( false )
|
||||
, m_changeOnNext( false )
|
||||
, m_firstTrackGenerated( false )
|
||||
, m_filterUnresolvable( true )
|
||||
, m_currentAttempts( 0 )
|
||||
, m_lastResolvedRow( 0 )
|
||||
{
|
||||
@ -45,9 +46,11 @@ DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
|
||||
}
|
||||
m_playlist = playlist;
|
||||
|
||||
if( m_playlist->mode() == OnDemand )
|
||||
setFilterUnresolvable( true );
|
||||
|
||||
connect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) );
|
||||
PlaylistModel::loadPlaylist( m_playlist, !m_onDemandRunning );
|
||||
PlaylistModel::loadPlaylist( m_playlist, m_playlist->mode() == Static );
|
||||
}
|
||||
|
||||
QString
|
||||
@ -62,25 +65,18 @@ DynamicModel::startOnDemand()
|
||||
{
|
||||
connect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) );
|
||||
|
||||
// delete all the tracks
|
||||
clear();
|
||||
|
||||
m_playlist->generator()->startOnDemand();
|
||||
|
||||
m_onDemandRunning = true;
|
||||
m_startOnResolved = true;
|
||||
m_startOnResolved = false; // not anymore---user clicks a track to start it
|
||||
m_currentAttempts = 0;
|
||||
m_lastResolvedRow = 0;
|
||||
m_lastResolvedRow = rowCount( QModelIndex() );
|
||||
}
|
||||
|
||||
void
|
||||
DynamicModel::newTrackGenerated( const Tomahawk::query_ptr& query )
|
||||
{
|
||||
if( m_onDemandRunning ) {
|
||||
if( !m_firstTrackGenerated ) {
|
||||
emit firstTrackGenerated();
|
||||
m_firstTrackGenerated = false;
|
||||
}
|
||||
connect( query.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( trackResolveFinished( bool ) ) );
|
||||
connect( query.data(), SIGNAL( resultsAdded( QList<Tomahawk::result_ptr> ) ), this, SLOT( trackResolved() ) );
|
||||
|
||||
@ -92,7 +88,6 @@ void
|
||||
DynamicModel::stopOnDemand( bool stopPlaying )
|
||||
{
|
||||
m_onDemandRunning = false;
|
||||
m_firstTrackGenerated = false;
|
||||
if( stopPlaying )
|
||||
AudioEngine::instance()->stop();
|
||||
|
||||
@ -156,6 +151,79 @@ DynamicModel::newTrackLoading()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DynamicModel::tracksGenerated( const QList< query_ptr > entries, int limitResolvedTo )
|
||||
{
|
||||
if( m_filterUnresolvable ) { // wait till we get them resolved
|
||||
m_limitResolvedTo = limitResolvedTo;
|
||||
filterUnresolved( entries );
|
||||
} else {
|
||||
addToPlaylist( entries, m_playlist->mode() == OnDemand ); // if ondemand, we're previewing, so clear old
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DynamicModel::filterUnresolved( const QList< query_ptr >& entries )
|
||||
{
|
||||
m_toResolveList = entries;
|
||||
|
||||
foreach( const query_ptr& q, entries ) {
|
||||
connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( filteringTrackResolved( bool ) ) );
|
||||
Pipeline::instance()->resolve( q );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DynamicModel::filteringTrackResolved( bool successful )
|
||||
{
|
||||
// arg, we don't have the query_ptr, just the Query
|
||||
Query* q = qobject_cast< Query* >( sender() );
|
||||
Q_ASSERT( q );
|
||||
|
||||
query_ptr realptr;
|
||||
foreach( const query_ptr& qptr, m_toResolveList ) {
|
||||
if( qptr.data() == q ) {
|
||||
realptr = qptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( realptr.isNull() ) // we already finished
|
||||
return;
|
||||
|
||||
m_toResolveList.removeAll( realptr );
|
||||
|
||||
if( successful )
|
||||
m_resolvedList << realptr;
|
||||
|
||||
if( m_toResolveList.isEmpty() || m_resolvedList.size() == m_limitResolvedTo ) { // done, add to playlist
|
||||
if( m_limitResolvedTo < m_resolvedList.count() ) // limit to how many we were asked for
|
||||
m_resolvedList = m_resolvedList.mid( 0, m_limitResolvedTo );
|
||||
|
||||
addToPlaylist( m_resolvedList, true );
|
||||
m_toResolveList.clear();
|
||||
m_resolvedList.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicModel::addToPlaylist( const QList< query_ptr >& entries, bool clearFirst )
|
||||
{
|
||||
if( clearFirst )
|
||||
clear();
|
||||
|
||||
if( m_playlist->author()->isLocal() && m_playlist->mode() == Static ) {
|
||||
m_playlist->addEntries( entries, m_playlist->currentrevision() );
|
||||
} else { // read-only, so add tracks only in the GUI, not to the playlist itself
|
||||
foreach( const query_ptr& query, entries ) {
|
||||
append( query );
|
||||
}
|
||||
}
|
||||
|
||||
emit tracksAdded();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicModel::removeIndex(const QModelIndex& idx, bool moreToCome)
|
||||
{
|
||||
|
@ -47,13 +47,19 @@ public:
|
||||
virtual void removeIndex( const QModelIndex& index, bool moreToCome = false );
|
||||
|
||||
bool searchingForNext() const { return m_searchingForNext; }
|
||||
|
||||
void setFilterUnresolvable( bool filter ) { m_filterUnresolvable = filter; }
|
||||
bool filterUnresolvable() const { return m_filterUnresolvable; }
|
||||
|
||||
// a batchof static tracks wre generated
|
||||
void tracksGenerated( const QList< query_ptr > entries, int limitResolvedTo = -1 );
|
||||
signals:
|
||||
void collapseFromTo( int startRow, int num );
|
||||
void checkForOverflow();
|
||||
|
||||
void firstTrackGenerated();
|
||||
void trackGenerationFailure( const QString& msg );
|
||||
|
||||
void tracksAdded();
|
||||
private slots:
|
||||
void newTrackGenerated( const Tomahawk::query_ptr& query );
|
||||
|
||||
@ -61,13 +67,23 @@ private slots:
|
||||
void trackResolved();
|
||||
void newTrackLoading();
|
||||
|
||||
void filteringTrackResolved( bool successful );
|
||||
private:
|
||||
void filterUnresolved( const QList< query_ptr >& entries );
|
||||
void addToPlaylist( const QList< query_ptr >& entries, bool clearFirst );
|
||||
|
||||
dynplaylist_ptr m_playlist;
|
||||
// for filtering unresolvable
|
||||
int m_limitResolvedTo;
|
||||
QList< query_ptr > m_toResolveList;
|
||||
QList< query_ptr > m_resolvedList;
|
||||
|
||||
bool m_startOnResolved;
|
||||
bool m_onDemandRunning;
|
||||
bool m_changeOnNext;
|
||||
bool m_searchingForNext;
|
||||
bool m_firstTrackGenerated;
|
||||
bool m_filterUnresolvable;
|
||||
int m_currentAttempts;
|
||||
int m_lastResolvedRow;
|
||||
};
|
||||
|
@ -29,10 +29,10 @@ Tomahawk::EchonestControl::EchonestControl( const QString& selectedType, const Q
|
||||
: DynamicControl ( selectedType.isEmpty() ? "Artist" : selectedType, typeSelectors, parent )
|
||||
{
|
||||
setType( "echonest" );
|
||||
m_editingTimer.setInterval( 2000 ); // 2 second timeout to edits
|
||||
m_editingTimer.setInterval( 500 ); //timeout to edits
|
||||
m_editingTimer.setSingleShot( true );
|
||||
|
||||
connect( &m_editingTimer, SIGNAL( timeout() ), this, SIGNAL( changed() ) );
|
||||
connect( &m_editingTimer, SIGNAL( timeout() ), this, SLOT( editTimerFired() ) );
|
||||
updateWidgets();
|
||||
}
|
||||
|
||||
@ -340,7 +340,6 @@ Tomahawk::EchonestControl::setupMinMaxWidgets( Echonest::DynamicPlaylist::Playli
|
||||
connect( match, SIGNAL( activated( int ) ), this, SLOT( editingFinished() ) );
|
||||
connect( input->slider(), SIGNAL( valueChanged( int ) ), this, SLOT( updateData() ) );
|
||||
connect( input->slider(), SIGNAL( valueChanged( int ) ), this, SLOT( editingFinished() ) );
|
||||
connect( input->slider(), SIGNAL( sliderMoved( int ) ), &m_editingTimer, SLOT( stop() ) );
|
||||
|
||||
match->hide();
|
||||
input->hide();
|
||||
@ -491,6 +490,18 @@ Tomahawk::EchonestControl::editingFinished()
|
||||
m_editingTimer.start();
|
||||
}
|
||||
|
||||
void
|
||||
Tomahawk::EchonestControl::editTimerFired()
|
||||
{
|
||||
// make sure it's really changed
|
||||
if( m_cacheData != m_data.second ) { // new, so emit changed
|
||||
emit changed();
|
||||
}
|
||||
|
||||
m_cacheData = m_data.second;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Tomahawk::EchonestControl::calculateSummary()
|
||||
{
|
||||
|
@ -52,6 +52,7 @@ public slots:
|
||||
private slots:
|
||||
void updateData();
|
||||
void editingFinished();
|
||||
void editTimerFired();
|
||||
|
||||
private:
|
||||
void updateWidgets();
|
||||
@ -79,6 +80,7 @@ private:
|
||||
QTimer m_editingTimer;
|
||||
|
||||
Echonest::DynamicPlaylist::PlaylistParamData m_data;
|
||||
QVariant m_cacheData;
|
||||
|
||||
friend class EchonestGenerator;
|
||||
};
|
||||
|
@ -45,13 +45,15 @@ public:
|
||||
|
||||
void setPlaylist( const dynplaylist_ptr& playlist );
|
||||
|
||||
void fadeIn();
|
||||
void fadeOut();
|
||||
|
||||
qreal opacity() const { return m_opacity; }
|
||||
void setOpacity( qreal opacity );
|
||||
|
||||
virtual void paintEvent( QPaintEvent* );
|
||||
|
||||
public slots:
|
||||
void fadeIn();
|
||||
void fadeOut();
|
||||
|
||||
signals:
|
||||
void generatePressed( int num );
|
||||
void typeChanged( const QString& playlistType );
|
||||
|
@ -68,13 +68,15 @@ DynamicWidget::DynamicWidget( const Tomahawk::dynplaylist_ptr& playlist, QWidget
|
||||
|
||||
connect( m_model, SIGNAL( collapseFromTo( int, int ) ), m_view, SLOT( collapseEntries( int, int ) ) );
|
||||
connect( m_model, SIGNAL( trackGenerationFailure( QString ) ), this, SLOT( stationFailed( QString ) ) );
|
||||
connect( m_model, SIGNAL( firstTrackGenerated() ), this, SLOT( firstStationTrackGenerated() ) );
|
||||
|
||||
m_loading = new LoadingSpinner( m_view );
|
||||
connect( m_model, SIGNAL( tracksAdded() ), m_loading, SLOT( fadeOut() ) );
|
||||
|
||||
m_setup = new DynamicSetupWidget( playlist, this );
|
||||
m_setup->fadeIn();
|
||||
|
||||
connect( m_model, SIGNAL( tracksAdded() ), m_setup, SLOT( fadeOut() ) );
|
||||
|
||||
loadDynamicPlaylist( playlist );
|
||||
|
||||
m_layout->setContentsMargins( 0, 0, 0, 0 );
|
||||
@ -90,8 +92,7 @@ DynamicWidget::DynamicWidget( const Tomahawk::dynplaylist_ptr& playlist, QWidget
|
||||
connect( m_controls, SIGNAL( controlChanged( Tomahawk::dyncontrol_ptr ) ), this, SLOT( controlChanged( Tomahawk::dyncontrol_ptr ) ), Qt::QueuedConnection );
|
||||
connect( m_controls, SIGNAL( controlsChanged() ), this, SLOT( controlsChanged() ), Qt::QueuedConnection );
|
||||
|
||||
connect( PlaylistManager::instance(), SIGNAL( playClicked() ), this, SLOT( playPressed() ) );
|
||||
connect( PlaylistManager::instance(), SIGNAL( pauseClicked() ), this, SLOT( pausePressed() ) );
|
||||
connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), this, SLOT( trackStarted() ) );
|
||||
connect( AudioEngine::instance(), SIGNAL( playlistChanged( PlaylistInterface* ) ), this, SLOT( playlistStopped( PlaylistInterface* ) ) );
|
||||
}
|
||||
|
||||
@ -153,6 +154,9 @@ DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
|
||||
if( !m_playlist.isNull() )
|
||||
m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
|
||||
|
||||
if( m_playlist->mode() == OnDemand )
|
||||
showPreview();
|
||||
|
||||
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( error( QString, QString ) ), this, SLOT( generatorError( QString, QString ) ) );
|
||||
@ -232,15 +236,10 @@ DynamicWidget::showEvent(QShowEvent* )
|
||||
void
|
||||
DynamicWidget::generate( int num )
|
||||
{
|
||||
if( m_playlist->mode() == Static )
|
||||
{
|
||||
// get the items from the generator, and put them in the playlist
|
||||
m_view->setDynamicWorking( true );
|
||||
m_loading->fadeIn();
|
||||
m_playlist->generator()->generate( num );
|
||||
} else if( m_playlist->mode() == OnDemand ) {
|
||||
|
||||
}
|
||||
// get the items from the generator, and put them in the playlist
|
||||
m_view->setDynamicWorking( true );
|
||||
m_loading->fadeIn();
|
||||
m_playlist->generator()->generate( num );
|
||||
}
|
||||
|
||||
void
|
||||
@ -253,36 +252,16 @@ DynamicWidget::stationFailed( const QString& msg )
|
||||
stopStation( false );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicWidget::pausePressed()
|
||||
{
|
||||
// we don't handle explicit pausing right now
|
||||
// no more track plays == no more adding. we stop when
|
||||
// the user switches to a different playlist.
|
||||
}
|
||||
|
||||
void
|
||||
DynamicWidget::playPressed()
|
||||
{
|
||||
|
||||
DynamicWidget::trackStarted()
|
||||
{
|
||||
if( isVisible() && !m_playlist.isNull() &&
|
||||
m_playlist->mode() == OnDemand && !m_runningOnDemand ) {
|
||||
|
||||
m_view->setDynamicWorking( true );
|
||||
startStation();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
DynamicWidget::firstStationTrackGenerated()
|
||||
{
|
||||
m_view->setDynamicWorking( false );
|
||||
m_loading->fadeOut();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicWidget::stopStation( bool stopPlaying )
|
||||
{
|
||||
@ -328,17 +307,16 @@ DynamicWidget::playlistTypeChanged( QString )
|
||||
|
||||
void
|
||||
DynamicWidget::tracksGenerated( const QList< query_ptr >& queries )
|
||||
{
|
||||
m_loading->fadeOut();
|
||||
|
||||
if( m_playlist->author()->isLocal() ) {
|
||||
m_playlist->addEntries( queries, m_playlist->currentrevision() );
|
||||
{
|
||||
int limit = -1; // only limit the "preview" of a station
|
||||
if( m_playlist->author()->isLocal() && m_playlist->mode() == Static ) {
|
||||
m_resolveOnNextLoad = true;
|
||||
} else { // read-only, so add tracks only in the GUI, not to the playlist itself
|
||||
foreach( const query_ptr& query, queries ) {
|
||||
m_model->append( query );
|
||||
}
|
||||
}
|
||||
} else if( m_playlist->mode() == OnDemand )
|
||||
limit = 5;
|
||||
|
||||
if( m_playlist->mode() != OnDemand )
|
||||
m_loading->fadeOut();
|
||||
m_model->tracksGenerated( queries, limit );
|
||||
}
|
||||
|
||||
|
||||
@ -348,8 +326,6 @@ DynamicWidget::controlsChanged()
|
||||
// controlsChanged() is emitted when a control is added or removed
|
||||
// in the case of addition, it's blank by default... so to avoid an error
|
||||
// when playing a station just ignore it till we're ready and get a controlChanged()
|
||||
/* if( m_runningOnDemand )
|
||||
m_model->changeStation();*/
|
||||
m_controlsChanged = true;
|
||||
|
||||
if( !m_playlist->author()->isLocal() )
|
||||
@ -365,8 +341,19 @@ DynamicWidget::controlChanged( const Tomahawk::dyncontrol_ptr& control )
|
||||
return;
|
||||
m_playlist->createNewRevision();
|
||||
m_seqRevLaunched++;
|
||||
|
||||
showPreview();
|
||||
}
|
||||
|
||||
void
|
||||
DynamicWidget::showPreview()
|
||||
{
|
||||
if( m_playlist->mode() == OnDemand && !m_runningOnDemand ) { // if this is a not running station, preview matching tracks
|
||||
generate( 40 ); // ask for more, we'll filter how many we actually want
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicWidget::generatorError( const QString& title, const QString& content )
|
||||
{
|
||||
|
@ -85,8 +85,7 @@ public slots:
|
||||
void startStation();
|
||||
void stopStation( bool stopPlaying = true );
|
||||
|
||||
void playPressed();
|
||||
void pausePressed();
|
||||
void trackStarted();
|
||||
void stationFailed( const QString& );
|
||||
|
||||
void playlistStopped( PlaylistInterface* );
|
||||
@ -95,10 +94,10 @@ private slots:
|
||||
void generate( int = -1 );
|
||||
void tracksGenerated( const QList< Tomahawk::query_ptr>& queries );
|
||||
void generatorError( const QString& title, const QString& content );
|
||||
void firstStationTrackGenerated();
|
||||
|
||||
void controlsChanged();
|
||||
void controlChanged( const Tomahawk::dyncontrol_ptr& control );
|
||||
void showPreview();
|
||||
|
||||
void layoutFloatingWidgets();
|
||||
|
||||
|
@ -104,7 +104,7 @@ LoadingSpinner::paintEvent( QPaintEvent* ev )
|
||||
{
|
||||
QPainter p( this );
|
||||
|
||||
qDebug() << "FADING" << ( m_showHide->state() == QTimeLine::Running ) << "at frame:" << m_showHide->currentValue();
|
||||
// qDebug() << "FADING" << ( m_showHide->state() == QTimeLine::Running ) << "at frame:" << m_showHide->currentValue();
|
||||
if( m_showHide->state() == QTimeLine::Running ) { // showing or hiding
|
||||
p.setOpacity( (qreal)m_showHide->currentValue() );
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ public:
|
||||
virtual void paintEvent( QPaintEvent* );
|
||||
virtual void resizeEvent( QResizeEvent* );
|
||||
|
||||
public slots:
|
||||
void fadeIn();
|
||||
void fadeOut();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user