1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-03-20 07:49:42 +01:00

Handle catalogs more gracefully in dynamic playlists

Standardize some code

Propertify

Misc catalog fixes

Clean up by hacking
This commit is contained in:
Leo Franchi 2011-09-30 15:35:38 -04:00
parent 5f9c21120d
commit 625dc0304b
8 changed files with 156 additions and 45 deletions

View File

@ -369,5 +369,12 @@ EchonestCatalogSynchronizer::trackAttributes( PairList attributes )
QByteArray
EchonestCatalogSynchronizer::escape( const QString &in ) const
{
return QUrl::toPercentEncoding( in );
// TODO echonest chokes on some chars in the output. But if we percent-encode those chars it works
// We can't percent-encode the whole string, because then any UTF-8 chars that have been url-encoded, fail.
// God this sucks. It's going to break...
QString clean = in;
clean.replace( "&", "%25" );
clean.replace( ";", "%3B" );
return clean.toUtf8();
//return QUrl::toPercentEncoding( in. );
}

View File

@ -50,6 +50,7 @@ public:
Echonest::Catalog artistCatalog() const { return m_artistCatalog; }
signals:
void knownCatalogsChanged();
private slots:
void checkSettingsChanged();
@ -83,6 +84,8 @@ private:
QQueue< QList< QPair< QID, QString > > > m_queuedTrackInfo;
static EchonestCatalogSynchronizer* s_instance;
friend class ::DatabaseCommand_SetCollectionAttributes;
};
}

View File

@ -21,6 +21,7 @@
#include "source.h"
#include "network/servent.h"
#include "sourcelist.h"
#include "EchonestCatalogSynchronizer.h"
DatabaseCommand_SetCollectionAttributes::DatabaseCommand_SetCollectionAttributes( AttributeType type, const QByteArray& id )
: DatabaseCommandLoggable( )
@ -72,5 +73,10 @@ DatabaseCommand_SetCollectionAttributes::exec( DatabaseImpl *lib )
void
DatabaseCommand_SetCollectionAttributes::postCommitHook()
{
Servent::instance()->triggerDBSync();
if ( m_type == EchonestSongCatalog ||
m_type == EchonestArtistCatalog )
Tomahawk::EchonestCatalogSynchronizer::instance()->knownCatalogsChanged();
if ( source()->isLocal() )
Servent::instance()->triggerDBSync();
}

View File

@ -28,6 +28,7 @@ class DatabaseCommand_SetCollectionAttributes : public DatabaseCommandLoggable
Q_OBJECT
Q_PROPERTY( QByteArray id READ id WRITE setId )
Q_PROPERTY( int type READ type WRITE setType )
Q_PROPERTY( bool del READ del WRITE setDel )
public:
enum AttributeType {
@ -38,8 +39,7 @@ public:
DatabaseCommand_SetCollectionAttributes( AttributeType type, const QByteArray& id );
// Delete all attributes for the source+type
DatabaseCommand_SetCollectionAttributes( AttributeType type, bool toDelete );
DatabaseCommand_SetCollectionAttributes() {} // JSON
DatabaseCommand_SetCollectionAttributes() : m_delete( false ) {} // JSON
virtual void exec( DatabaseImpl* lib );
virtual bool doesMutates() const { return true; }
virtual void postCommitHook();
@ -52,6 +52,8 @@ public:
void setType( int type ) { m_type = (AttributeType)type; }
int type() const { return (int)m_type; }
void setDel( bool del ) { m_delete = del; }
bool del() const { return m_delete; }
private:
bool m_delete;
AttributeType m_type;

View File

@ -29,6 +29,7 @@
#include "EchonestGenerator.h"
#include "utils/logger.h"
#include <sourcelist.h>
QHash< QString, QStringList > Tomahawk::EchonestControl::s_suggestCache = QHash< QString, QStringList >();
@ -198,7 +199,7 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide();
m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input );
} else if( selectedType() == "Catalog Radio" ) {
} else if( selectedType() == "User Radio" ) {
m_currentType = Echonest::DynamicPlaylist::SourceCatalog;
QLabel* match = new QLabel( tr( "from user" ) );
@ -209,6 +210,12 @@ Tomahawk::EchonestControl::updateWidgets()
combo->addItem( str, EchonestGenerator::catalogId( str ) );
}
if ( EchonestGenerator::userCatalogs().isEmpty() )
combo->addItem( tr( "No users with Echo Nest Catalogs enabled. Try enabling option in Collection settings" ) );
if ( combo->findData( m_data.second ) < 0 )
combo->setCurrentIndex( 0 );
m_matchString = match->text();
m_matchData = match->text();
@ -493,7 +500,7 @@ Tomahawk::EchonestControl::updateData()
updateFromComboAndSlider();
} else if( selectedType() == "Danceability" || selectedType() == "Energy" || selectedType() == "Artist Familiarity" || selectedType() == "Artist Hotttnesss" || selectedType() == "Song Hotttnesss" ) {
updateFromComboAndSlider( true );
} else if( selectedType() == "Mode" || selectedType() == "Key" || selectedType() == "Mood" || selectedType() == "Style" || selectedType() == "Catalog Radio" ) {
} else if( selectedType() == "Mode" || selectedType() == "Key" || selectedType() == "Mood" || selectedType() == "Style" || selectedType() == "User Radio" ) {
updateFromLabelAndCombo();
} else if( selectedType() == "Sorting" ) {
QComboBox* match = qobject_cast<QComboBox*>( m_match.data() );
@ -556,6 +563,26 @@ Tomahawk::EchonestControl::updateWidgetsFromData()
QLineEdit* edit = qobject_cast<QLineEdit*>( m_input.data() );
if( edit )
edit->setText( m_data.second.toString() );
} else if ( selectedType() == "User Radio" )
{
QComboBox* combo = qobject_cast< QComboBox* >( m_input.data() );
if ( combo )
{
combo->clear();
foreach( const QString& str, EchonestGenerator::userCatalogs() )
{
combo->addItem( str, EchonestGenerator::catalogId( str ) );
}
if ( EchonestGenerator::userCatalogs().isEmpty() )
combo->addItem( tr( "No users with Echo Nest Catalogs enabled. Try enabling option in Collection settings" ) );
if ( combo->findData( m_data.second ) < 0 )
combo->setCurrentIndex( 0 );
combo->setCurrentIndex( combo->findData( m_data.second ) );
}
} else if( selectedType() == "Variety" || selectedType() == "Adventurousness" ) {
LabeledSlider* s = qobject_cast<LabeledSlider*>( m_input.data() );
if( s )
@ -564,7 +591,7 @@ Tomahawk::EchonestControl::updateWidgetsFromData()
updateToComboAndSlider();
} else if( selectedType() == "Danceability" || selectedType() == "Energy" || selectedType() == "Artist Familiarity" || selectedType() == "Artist Hotttnesss" || selectedType() == "Song Hotttnesss" ) {
updateToComboAndSlider( true );
} else if( selectedType() == "Mode" || selectedType() == "Key" || selectedType() == "Mood" || selectedType() == "Style" || selectedType() == "Catalog Radio" ) {
} else if( selectedType() == "Mode" || selectedType() == "Key" || selectedType() == "Mood" || selectedType() == "Style") {
updateToLabelAndCombo();
} else if( selectedType() == "Sorting" ) {
QComboBox* match = qobject_cast<QComboBox*>( m_match.data() );
@ -714,6 +741,24 @@ Tomahawk::EchonestControl::calculateSummary()
summary = QString( "similar to ~%1" ).arg( m_data.second.toString() );
} else if( selectedType() == "Artist Description" ) {
summary = QString( "with genre ~%1" ).arg( m_data.second.toString() );
} else if( selectedType() == "User Radio" ) {
QComboBox* b = qobject_cast< QComboBox* >( m_input.data() );
if ( b )
{
if ( b->currentText().isEmpty() || b->itemData( b->currentIndex() ).isNull() )
summary = "from no one";
else
{
QString subSum;
if ( b->currentText() == "My Collection" )
subSum = "my";
else
subSum = b->currentText();
summary = QString( "from %1 radio" ).arg( subSum );
}
}
else
summary = "from no one";
} else if( selectedType() == "Artist Description" || selectedType() == "Song" ) {
summary = QString( "similar to ~%1" ).arg( m_data.second.toString() );
} else if( selectedType() == "Variety" || selectedType() == "Danceability" || selectedType() == "Artist Hotttnesss" || selectedType() == "Energy" || selectedType() == "Artist Familiarity" || selectedType() == "Song Hotttnesss" ) {

View File

@ -22,6 +22,7 @@
#include <echonest/Playlist.h>
#include "dynamic/DynamicControl.h"
#include <QTimer>
namespace Tomahawk

View File

@ -28,6 +28,7 @@
#include "sourcelist.h"
#include <QFile>
#include <QDir>
#include <EchonestCatalogSynchronizer.h>
using namespace Tomahawk;
@ -37,8 +38,7 @@ QStringList EchonestGenerator::s_styles = QStringList();
QNetworkReply* EchonestGenerator::s_moodsJob = 0;
QNetworkReply* EchonestGenerator::s_stylesJob = 0;
bool EchonestGenerator::s_catalogsFetched = false;
QHash< QString, QString > EchonestGenerator::s_catalogs = QHash< QString, QString >();
CatalogManager* EchonestGenerator::s_catalogs = 0;
EchonestFactory::EchonestFactory()
@ -63,18 +63,54 @@ EchonestFactory::createControl( const QString& controlType )
QStringList
EchonestFactory::typeSelectors() const
{
QStringList types = QStringList() << "Artist" << "Artist Description" << "Song" << "Mood" << "Style" << "Variety" << "Tempo" << "Duration" << "Loudness"
QStringList types = QStringList() << "Artist" << "Artist Description" << "User Radio" << "Song" << "Mood" << "Style" << "Variety" << "Tempo" << "Duration" << "Loudness"
<< "Danceability" << "Energy" << "Artist Familiarity" << "Artist Hotttnesss" << "Song Hotttnesss"
<< "Longitude" << "Latitude" << "Mode" << "Key" << "Sorting";
if ( TomahawkSettings::instance()->enableEchonestCatalogs() )
{
types.insert( 2, "Catalog Radio" );
types.insert( 3, "Adventurousness" );
}
return types;
}
CatalogManager::CatalogManager( QObject* parent )
: QObject( parent )
{
connect( EchonestCatalogSynchronizer::instance(), SIGNAL( knownCatalogsChanged() ), this, SLOT( doCatalogUpdate() ) );
connect( SourceList::instance(), SIGNAL( ready() ), this, SLOT( doCatalogUpdate() ) );
doCatalogUpdate();
}
void
CatalogManager::collectionAttributes( const PairList& data )
{
QPair<QString, QString> part;
m_catalogs.clear();
foreach ( part, data )
{
if ( SourceList::instance()->get( part.first.toInt() ).isNull() )
continue;
const QString name = SourceList::instance()->get( part.first.toInt() )->friendlyName();
m_catalogs.insert( name, part.second );
}
emit catalogsUpdated();
}
void
CatalogManager::doCatalogUpdate()
{
QSharedPointer< DatabaseCommand > cmd( new DatabaseCommand_CollectionAttributes( DatabaseCommand_SetCollectionAttributes::EchonestSongCatalog ) );
connect( cmd.data(), SIGNAL( collectionAttributes( PairList ) ), this, SLOT( collectionAttributes( PairList ) ) );
Database::instance()->enqueue( cmd );
}
QHash< QString, QString >
CatalogManager::catalogs() const
{
return m_catalogs;
}
EchonestGenerator::EchonestGenerator ( QObject* parent )
: GeneratorInterface ( parent )
@ -86,17 +122,13 @@ EchonestGenerator::EchonestGenerator ( QObject* parent )
m_logo.load( RESPATH "/images/echonest_logo.png" );
loadStylesAndMoods();
if ( s_catalogs.isEmpty() && TomahawkSettings::instance()->enableEchonestCatalogs() )
{
if ( !s_catalogsFetched )
{
QSharedPointer< DatabaseCommand > cmd( new DatabaseCommand_CollectionAttributes( DatabaseCommand_SetCollectionAttributes::EchonestSongCatalog ) );
connect( cmd.data(), SIGNAL(collectionAttributes(PairList)),
this, SLOT(collectionAttributes(PairList) ) );
Database::instance()->enqueue( cmd );
s_catalogsFetched = true;
}
}
// TODO Yes this is a race condition. If multiple threads initialize echonestgenerator at the exact same time we could run into some issues.
// not dealing with that right now.
if ( s_catalogs == 0 )
s_catalogs = new CatalogManager( this );
connect( s_catalogs, SIGNAL( catalogsUpdated() ), this, SLOT( knownCatalogsChanged() ) );
// qDebug() << "ECHONEST:" << m_logo.size();
}
@ -120,6 +152,16 @@ QPixmap EchonestGenerator::logo()
return m_logo;
}
void
EchonestGenerator::knownCatalogsChanged()
{
// Refresh all contrls
foreach( const dyncontrol_ptr& control, m_controls )
{
control.staticCast< EchonestControl >()->updateWidgetsFromData();
}
}
void
EchonestGenerator::generate( int number )
@ -381,30 +423,17 @@ EchonestGenerator::resetSteering()
m_steerData.second = QString();
}
void
EchonestGenerator::collectionAttributes(PairList data)
{
QPair<QString, QString> part;
foreach ( part, data )
{
if ( SourceList::instance()->get( part.first.toInt() ).isNull() )
continue;
const QString name = SourceList::instance()->get( part.first.toInt() )->friendlyName();
s_catalogs.insert( name, part.second );
}
}
QByteArray
EchonestGenerator::catalogId(const QString &collectionId)
{
return s_catalogs.value( collectionId ).toUtf8();
return s_catalogs->catalogs().value( collectionId ).toUtf8();
}
QStringList
EchonestGenerator::userCatalogs()
{
return s_catalogs.keys();
return s_catalogs->catalogs().keys();
}
bool
@ -446,7 +475,7 @@ EchonestGenerator::appendRadioType( Echonest::DynamicPlaylist::PlaylistParams& p
/// 5. song-radio: If all the artist entries are Similar To. If some were but not all, error out.
bool someCatalog = false;
foreach( const dyncontrol_ptr& control, m_controls ) {
if ( control->selectedType() == "Catalog Radio" )
if ( control->selectedType() == "User Radio" )
someCatalog = true;
}
if( someCatalog )

View File

@ -34,6 +34,25 @@ namespace Tomahawk
class EchonestSteerer;
class CatalogManager : public QObject
{
Q_OBJECT
public:
CatalogManager( QObject* parent );
QHash< QString, QString > catalogs() const;
signals:
void catalogsUpdated();
private slots:
void doCatalogUpdate();
void collectionAttributes( const PairList& );
private:
QHash< QString, QString > m_catalogs;
};
class DLLEXPORT EchonestFactory : public GeneratorFactoryInterface
{
public:
@ -83,7 +102,7 @@ private slots:
void stylesReceived();
void moodsReceived();
void collectionAttributes(PairList);
void knownCatalogsChanged();
void songLookupFinished();
private:
@ -105,8 +124,7 @@ private:
static QNetworkReply* s_stylesJob;
static QNetworkReply* s_moodsJob;
static bool s_catalogsFetched;
static QHash< QString, QString > s_catalogs;
static CatalogManager* s_catalogs;
// used for the intermediary song id lookup
QSet< QNetworkReply* > m_waiting;