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

Add dbmigrate scrip, other misc work

This commit is contained in:
Leo Franchi
2011-09-25 08:16:50 -04:00
parent d60b38b3aa
commit d7e68f3a1d
23 changed files with 683 additions and 19 deletions

View File

@@ -0,0 +1,10 @@
-- Script to migate from db version 6 to 27
-- Nothing to do
CREATE TABLE IF NOT EXISTS collection_attributes (
id INTEGER REFERENCES source(id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, -- source id, null for local source
k TEXT NOT NULL,
v TEXT NOT NULL
);
UPDATE settings SET v = '27' WHERE k == 'schema_version';

View File

@@ -108,6 +108,7 @@
<file>data/sql/dbmigrate-23_to_24.sql</file>
<file>data/sql/dbmigrate-24_to_25.sql</file>
<file>data/sql/dbmigrate-25_to_26.sql</file>
<file>data/sql/dbmigrate-26_to_27.sql</file>
<file>data/js/tomahawk.js</file>
<file>data/images/avatar_frame.png</file>
<file>data/images/drop-all-songs.png</file>

View File

@@ -35,6 +35,8 @@ set( libSources
playlistinterface.cpp
LatchManager.cpp
EchonestCatalogSynchronizer.cpp
sip/SipPlugin.cpp
sip/SipHandler.cpp
sip/SipModel.cpp
@@ -95,6 +97,8 @@ set( libSources
database/databasecommand_socialaction.cpp
database/databasecommand_loadsocialactions.cpp
database/databasecommand_genericselect.cpp
database/databasecommand_setcollectionattributes.cpp
database/databasecommand_collectionattributes.cpp
database/database.cpp
infobar/infobar.cpp
@@ -253,6 +257,8 @@ set( libHeaders
album.h
playlist.h
EchonestCatalogSynchronizer.h
sip/SipPlugin.h
sip/SipHandler.h
sip/SipModel.h
@@ -313,6 +319,8 @@ set( libHeaders
database/databasecommand_socialaction.h
database/databasecommand_loadsocialactions.h
database/databasecommand_genericselect.h
database/databasecommand_setcollectionattributes.h
database/databasecommand_collectionattributes.h
infobar/infobar.h

View File

@@ -0,0 +1,195 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "EchonestCatalogSynchronizer.h"
#include "database/database.h"
#include "database/databasecommand_genericselect.h"
#include "database/databasecommand_setcollectionattributes.h"
#include "tomahawksettings.h"
#include "sourcelist.h"
#include <echonest/CatalogUpdateEntry.h>
#include <echonest/Config.h>
using namespace Tomahawk;
EchonestCatalogSynchronizer* EchonestCatalogSynchronizer::s_instance = 0;
EchonestCatalogSynchronizer::EchonestCatalogSynchronizer( QObject *parent )
: QObject( parent )
{
m_syncing = TomahawkSettings::instance()->enableEchonestCatalogs();
qRegisterMetaType<QList<QStringList> >("QList<QStringList>");
connect( TomahawkSettings::instance(), SIGNAL( changed() ), this, SLOT( checkSettingsChanged() ) );
const QByteArray artist = TomahawkSettings::instance()->value( "collection/artistCatalog" ).toByteArray();
const QByteArray song = TomahawkSettings::instance()->value( "collection/songCatalog" ).toByteArray();
if ( !artist.isEmpty() )
m_artistCatalog.setId( artist );
if ( !song.isEmpty() )
m_songCatalog.setId( song );
}
void
EchonestCatalogSynchronizer::checkSettingsChanged()
{
if ( TomahawkSettings::instance()->enableEchonestCatalogs() && !m_syncing )
{
// enable, and upload whole db
m_syncing = true;
tDebug() << "Echonest Catalog sync pref changed, uploading!!";
uploadDb();
} else if ( !TomahawkSettings::instance()->enableEchonestCatalogs() && m_syncing )
{
m_songCatalog.deleteCatalog();
m_artistCatalog.deleteCatalog();
m_syncing = false;
}
}
void
EchonestCatalogSynchronizer::uploadDb()
{
// create two catalogs: uuid_song, and uuid_artist.
QNetworkReply* r = Echonest::Catalog::create( QString( "%1_song" ).arg( Database::instance()->dbid() ), Echonest::CatalogTypes::Song );
connect( r, SIGNAL( finished() ), this, SLOT( songCreateFinished() ) );
r = Echonest::Catalog::create( QString( "%1_artist" ).arg( Database::instance()->dbid() ), Echonest::CatalogTypes::Artist );
connect( r, SIGNAL( finished() ), this, SLOT( artistCreateFinished() ) );
}
void
EchonestCatalogSynchronizer::songCreateFinished()
{
QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() );
Q_ASSERT( r );
tDebug() << "Finished creating song catalog, updating data now!!";
try
{
m_songCatalog = Echonest::Catalog::parseCreate( r );
TomahawkSettings::instance()->setValue( "collection/songCatalog", m_songCatalog.id() );
QSharedPointer< DatabaseCommand > cmd( new DatabaseCommand_SetCollectionAttributes( SourceList::instance()->getLocal(),
DatabaseCommand_SetCollectionAttributes::EchonestSongCatalog,
m_songCatalog.id() ) );
Database::instance()->enqueue( cmd );
} catch ( const Echonest::ParseError& e )
{
tLog() << "Echonest threw an exception parsing song catalog create:" << e.what();
return;
}
QString sql( "SELECT track.name, artist.name, album.name "
"FROM file, artist, track, file_join "
"LEFT OUTER JOIN album "
"ON file_join.album = album.id "
"WHERE file.id = file_join.file "
"AND file_join.artist = artist.id "
"AND file_join.track = track.id "
"AND file.source IS NULL");
DatabaseCommand_GenericSelect* cmd = new DatabaseCommand_GenericSelect( sql, DatabaseCommand_GenericSelect::Track, true );
connect( cmd, SIGNAL( rawData( QList< QStringList > ) ), this, SLOT( rawTracks( QList< QStringList > ) ) );
Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) );
}
void
EchonestCatalogSynchronizer::artistCreateFinished()
{
QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() );
Q_ASSERT( r );
try
{
m_artistCatalog = Echonest::Catalog::parseCreate( r );
TomahawkSettings::instance()->setValue( "collection/artistCatalog", m_artistCatalog.id() );
// QSharedPointer< DatabaseCommand > cmd( new DatabaseCommand_SetCollectionAttributes( SourceList::instance()->getLocal(),
// DatabaseCommand_SetCollectionAttributes::EchonestSongCatalog,
// m_songCatalog.id() ) );
// Database::instance()->enqueue( cmd );
} catch ( const Echonest::ParseError& e )
{
tLog() << "Echonest threw an exception parsing artist catalog create:" << e.what();
return;
}
}
void
EchonestCatalogSynchronizer::rawTracks( const QList< QStringList >& tracks )
{
tDebug() << "Got raw tracks, num:" << tracks.size();
Echonest::CatalogUpdateEntries entries( tracks.size() );
for ( int i = 0; i < tracks.size(); i++ )
{
if ( tracks[ i ][ 0 ].isEmpty() || tracks[ i ][ 1 ].isEmpty() )
continue;
Echonest::CatalogUpdateEntry entry;
entry.setAction( Echonest::CatalogTypes::Update );
entry.setSongName( tracks[ i ][ 0 ] );
entry.setArtistName( tracks[ i ][ 1 ] );
entry.setRelease( tracks[ i ][ 2 ] );
entry.setItemId( uuid().toUtf8() );
entries.append( entry );
}
QNetworkReply* updateJob = m_songCatalog.update( entries );
connect( updateJob, SIGNAL( finished() ), this, SLOT( songUpdateFinished() ) );
}
void
EchonestCatalogSynchronizer::songUpdateFinished()
{
QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() );
Q_ASSERT( r );
try
{
QByteArray ticket = m_songCatalog.parseTicket( r );
QNetworkReply* tJob = m_songCatalog.status( ticket );
connect( tJob, SIGNAL( finished() ), this, SLOT( checkTicket() ) );
} catch ( const Echonest::ParseError& e )
{
tLog() << "Echonest threw an exception parsing catalog update finished:" << e.what();
return;
}
}
void
EchonestCatalogSynchronizer::checkTicket()
{
QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() );
Q_ASSERT( r );
try
{
Echonest::CatalogStatus status = m_songCatalog.parseStatus( r );
tLog() << "Catalog status update:" << status.status << status.details << status.items;
} catch ( const Echonest::ParseError& e )
{
tLog() << "Echonest threw an exception parsing catalog create:" << e.what();
return;
}
}

View File

@@ -0,0 +1,71 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ECHONESTCATALOGSYNCHRONIZER_H
#define ECHONESTCATALOGSYNCHRONIZER_H
#include <echonest/Catalog.h>
#include <QObject>
namespace Tomahawk
{
class EchonestCatalogSynchronizer : public QObject
{
Q_OBJECT
public:
static EchonestCatalogSynchronizer* instance() {
if ( !s_instance )
{
s_instance = new EchonestCatalogSynchronizer;
}
return s_instance;
}
explicit EchonestCatalogSynchronizer(QObject *parent = 0);
Echonest::Catalog songCatalog() const { return m_songCatalog; }
Echonest::Catalog artistCatalog() const { return m_artistCatalog; }
signals:
private slots:
void checkSettingsChanged();
void songCreateFinished();
void artistCreateFinished();
void songUpdateFinished();
void checkTicket();
void rawTracks( const QList< QStringList >& tracks );
private:
void uploadDb();
bool m_syncing;
Echonest::Catalog m_songCatalog;
Echonest::Catalog m_artistCatalog;
static EchonestCatalogSynchronizer* s_instance;
};
}
#endif // ECHONESTCATALOGSYNCHRONIZER_H

View File

@@ -0,0 +1,59 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "DatabaseCommand_CollectionAttributes.h"
#include "databaseimpl.h"
#include "source.h"
DatabaseCommand_CollectionAttributes::DatabaseCommand_CollectionAttributes( DatabaseCommand_SetCollectionAttributes::AttributeType type )
: DatabaseCommand()
, m_type( type )
{
qRegisterMetaType< PairList >("PairList");
}
void
DatabaseCommand_CollectionAttributes::exec( DatabaseImpl *lib )
{
TomahawkSqlQuery query = lib->newquery();
// QString sourceStr;
// if ( source().isNull() )
// sourceStr = "id IS NULL";
// else
// sourceStr = QString( "id == %1" ).arg( source()->id() );
QString typeStr;
if ( m_type == DatabaseCommand_SetCollectionAttributes::EchonestSongCatalog )
typeStr = "echonest_song";
else if ( m_type == DatabaseCommand_SetCollectionAttributes::EchonestArtistCatalog )
typeStr = "echonest_artist";
QString queryStr = QString( "SELECT k, v FROM collection_attributes WHERE k = \"%1\"" ).arg( typeStr );
qDebug() << "Doing queryL" << queryStr;
query.exec( queryStr );
PairList data;
while ( query.next() )
{
QPair< QString, QString > part;
part.first = query.value( 0 ).toString();
part.second = query.value( 1 ).toString();
data << part;
}
emit data;
}

View File

@@ -0,0 +1,48 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DATABASECOMMAND_ECHONESTCATALOG_H
#define DATABASECOMMAND_ECHONESTCATALOG_H
#include "typedefs.h"
#include "databasecommand.h"
#include "databasecommand_setcollectionattributes.h"
#include <QByteArray>
typedef QList< QPair< QString, QString > > PairList;
class DatabaseCommand_CollectionAttributes : public DatabaseCommand
{
Q_OBJECT
public:
DatabaseCommand_CollectionAttributes( DatabaseCommand_SetCollectionAttributes::AttributeType type );
virtual void exec( DatabaseImpl* lib );
virtual bool doesMutates() const { return false; }
virtual QString commandname() const { return "setcollectionattributes"; }
signals:
void collectionAttributes( PairList );
private:
DatabaseCommand_SetCollectionAttributes::AttributeType m_type;
};
Q_DECLARE_METATYPE(PairList)
#endif

View File

@@ -32,9 +32,18 @@ DatabaseCommand_GenericSelect::DatabaseCommand_GenericSelect( const QString& sql
, m_sqlSelect( sqlSelect )
, m_queryType( type )
, m_limit( limit )
, m_raw( false )
{
}
DatabaseCommand_GenericSelect::DatabaseCommand_GenericSelect( const QString& sqlSelect, QueryType type, bool rawData, QObject* parent )
: DatabaseCommand( parent )
, m_sqlSelect( sqlSelect )
, m_queryType( type )
, m_limit( -1 )
, m_raw( rawData )
{
}
void
DatabaseCommand_GenericSelect::exec( DatabaseImpl* dbi )
@@ -48,6 +57,26 @@ DatabaseCommand_GenericSelect::exec( DatabaseImpl* dbi )
QList< artist_ptr > arts;
QList< album_ptr > albs;
if ( m_raw )
{
QList< QStringList > rawDataItems;
while( query.next() )
{
QStringList rawRow;
int count = 0;
while ( query.value( count ).isValid() )
{
rawRow << query.value( count ).toString();
++count;
}
rawDataItems << rawRow;
}
emit rawData( rawDataItems );
return;
}
// Expecting
while ( query.next() )
{

View File

@@ -24,6 +24,9 @@
#include "databasecommand.h"
#include "typedefs.h"
#include <QStringList>
#include <QMetaType>
#include "dllmacro.h"
/**
@@ -60,6 +63,7 @@ public:
};
explicit DatabaseCommand_GenericSelect( const QString& sqlSelect, QueryType type, int limitResults = -1, QObject* parent = 0 );
explicit DatabaseCommand_GenericSelect( const QString& sqlSelect, QueryType type, bool rawData, QObject* parent = 0 );
virtual void exec( DatabaseImpl* lib );
virtual bool doesMutates() const { return false; }
@@ -70,10 +74,14 @@ signals:
void artists( const QList< Tomahawk::artist_ptr >& artists );
void albums( const QList< Tomahawk::album_ptr >& albums );
void rawData( const QList< QStringList >& data );
private:
QString m_sqlSelect;
QueryType m_queryType;
int m_limit;
bool m_raw;
};
Q_DECLARE_METATYPE(QList<QStringList>);
#endif // DATABASECOMMAND_GENERICSELECT_H

View File

@@ -0,0 +1,50 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "DatabaseCommand_SetCollectionAttributes.h"
#include "databaseimpl.h"
#include "source.h"
DatabaseCommand_SetCollectionAttributes::DatabaseCommand_SetCollectionAttributes( const Tomahawk::source_ptr& source, AttributeType type, const QByteArray& id )
: DatabaseCommandLoggable( source )
, m_id( id )
, m_type( type )
{
}
void
DatabaseCommand_SetCollectionAttributes::exec( DatabaseImpl *lib )
{
TomahawkSqlQuery query = lib->newquery();
QString sourceStr;
if ( source().isNull() || source()->isLocal() )
sourceStr = "NULL";
else
sourceStr = QString( "%1" ).arg( source()->id() );
QString typeStr;
if ( m_type == EchonestSongCatalog )
typeStr = "echonest_song";
else if ( m_type == EchonestArtistCatalog )
typeStr = "echonest_artist";
QString queryStr = QString( "REPLACE INTO collection_attributes ( id, k, v ) VALUES( %1, \"%2\", \"%3\" )" ).arg( sourceStr ).arg( typeStr ).arg( QString::fromUtf8( m_id ) );
qDebug() << "Doing query:" << queryStr;
query.exec( queryStr );
}

View File

@@ -0,0 +1,45 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DATABASECOMMAND_SAVEECHONESTCATALOG_H
#define DATABASECOMMAND_SAVEECHONESTCATALOG_H
#include "typedefs.h"
#include "databasecommandloggable.h"
#include <QByteArray>
class DatabaseCommand_SetCollectionAttributes : public DatabaseCommandLoggable
{
public:
enum AttributeType {
EchonestSongCatalog = 0,
EchonestArtistCatalog = 1
};
DatabaseCommand_SetCollectionAttributes( const Tomahawk::source_ptr& source, AttributeType type, const QByteArray& id );
virtual void exec( DatabaseImpl* lib );
virtual bool doesMutates() const { return true; }
virtual QString commandname() const { return "saveechonestcatalog"; }
private:
AttributeType m_type;
QByteArray m_id;
};
#endif // DATABASECOMMAND_SAVEECHONESTCATALOG_H

View File

@@ -39,7 +39,7 @@
*/
#include "schema.sql.h"
#define CURRENT_SCHEMA_VERSION 26
#define CURRENT_SCHEMA_VERSION 27
DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent )

View File

@@ -228,6 +228,14 @@ CREATE TABLE IF NOT EXISTS track_attributes (
CREATE INDEX track_attrib_id ON track_attributes(id);
CREATE INDEX track_attrib_k ON track_attributes(k);
-- Collection attributes, tied to a source. An example might be an echonest song catalog
CREATE TABLE IF NOT EXISTS collection_attributes (
id INTEGER REFERENCES source(id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, -- source id, null for local source
k TEXT NOT NULL,
v TEXT NOT NULL
);
-- social attributes connected to the track.
-- like love, hate, comments, recommendations
-- love=[comment], hate=[comment], comment=Some text
@@ -283,4 +291,4 @@ CREATE TABLE IF NOT EXISTS settings (
v TEXT NOT NULL DEFAULT ''
);
INSERT INTO settings(k,v) VALUES('schema_version', '26');
INSERT INTO settings(k,v) VALUES('schema_version', '27');

View File

@@ -1,5 +1,5 @@
/*
This file was automatically generated from schema.sql on Mon Jul 25 20:38:55 EDT 2011.
This file was automatically generated from ./schema.sql on Sun Sep 25 10:36:01 EDT 2011.
*/
static const char * tomahawk_schema_sql =
@@ -152,6 +152,11 @@ static const char * tomahawk_schema_sql =
");"
"CREATE INDEX track_attrib_id ON track_attributes(id);"
"CREATE INDEX track_attrib_k ON track_attributes(k);"
"CREATE TABLE IF NOT EXISTS collection_attributes ("
" id INTEGER REFERENCES source(id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, "
" k TEXT NOT NULL,"
" v TEXT NOT NULL"
"};"
"CREATE TABLE IF NOT EXISTS social_attributes ("
" id INTEGER REFERENCES track(id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, "
" source INTEGER REFERENCES source(id) ON DELETE CASCADE ON UPDATE CASCADE, "
@@ -184,7 +189,7 @@ static const char * tomahawk_schema_sql =
" k TEXT NOT NULL PRIMARY KEY,"
" v TEXT NOT NULL DEFAULT ''"
");"
"INSERT INTO settings(k,v) VALUES('schema_version', '26');"
"INSERT INTO settings(k,v) VALUES('schema_version', '27');"
;
const char * get_tomahawk_sql()

View File

@@ -198,6 +198,29 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide();
m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input );
} else if( selectedType() == "Catalog Radio" ) {
m_currentType = Echonest::DynamicPlaylist::SourceCatalog;
QLabel* match = new QLabel( tr( "from user" ) );
QComboBox* combo = new QComboBox();
foreach( const QString& str, EchonestGenerator::userCatalogs() )
{
combo->addItem( str, EchonestGenerator::catalogId( str ) );
}
combo->addItem( "lfranchi@gmail.com", "CATNEQO132A15A6C7A" );
m_matchString = match->text();
m_matchData = match->text();
connect( combo, SIGNAL( activated( int ) ), this, SLOT( updateData() ) );
connect( combo, SIGNAL( activated( int ) ), this, SLOT( editingFinished() ) );
match->hide();
combo->hide();
m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( combo );
} else if( selectedType() == "Song" ) {
m_currentType = Echonest::DynamicPlaylist::SongId;
@@ -230,6 +253,27 @@ Tomahawk::EchonestControl::updateWidgets()
m_matchData = match->text();
connect( input->slider(), SIGNAL( valueChanged( int ) ), this, SLOT( updateData() ) );
connect( input->slider(), SIGNAL( valueChanged( int ) ), this, SLOT( editingFinished() ) );
match->hide();
input->hide();
m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input );
} else if( selectedType() == "Adventurousness" ) {
m_currentType = Echonest::DynamicPlaylist::Adventurousness;
QLabel* match = new QLabel( tr( "is" ) );
LabeledSlider* input = new LabeledSlider( tr( "Less" ), tr( "More" ) );
input->slider()->setRange( 0, 10000 );
input->slider()->setTickInterval( 1 );
input->slider()->setTracking( false );
input->slider()->setValue( 10000 * .2 );
m_matchString = match->text();
m_matchData = match->text();
connect( input->slider(), SIGNAL( valueChanged( int ) ), this, SLOT( updateData() ) );
connect( input->slider(), SIGNAL( valueChanged( int ) ), this, SLOT( editingFinished() ) );
@@ -440,7 +484,7 @@ Tomahawk::EchonestControl::updateData()
m_data.first = m_currentType;
m_data.second = edit->text();
}
} else if( selectedType() == "Variety" ) {
} else if( selectedType() == "Variety" || selectedType() == "Adventurousness" ) {
LabeledSlider* s = qobject_cast<LabeledSlider*>( m_input.data() );
if( s ) {
m_data.first = m_currentType;
@@ -450,7 +494,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" ) {
} else if( selectedType() == "Mode" || selectedType() == "Key" || selectedType() == "Mood" || selectedType() == "Style" || selectedType() == "Catalog Radio" ) {
updateFromLabelAndCombo();
} else if( selectedType() == "Sorting" ) {
QComboBox* match = qobject_cast<QComboBox*>( m_match.data() );
@@ -513,7 +557,7 @@ Tomahawk::EchonestControl::updateWidgetsFromData()
QLineEdit* edit = qobject_cast<QLineEdit*>( m_input.data() );
if( edit )
edit->setText( m_data.second.toString() );
} else if( selectedType() == "Variety" ) {
} else if( selectedType() == "Variety" || selectedType() == "Adventurousness" ) {
LabeledSlider* s = qobject_cast<LabeledSlider*>( m_input.data() );
if( s )
s->slider()->setValue( m_data.second.toDouble() * 10000 );
@@ -521,7 +565,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" ) {
} else if( selectedType() == "Mode" || selectedType() == "Key" || selectedType() == "Mood" || selectedType() == "Style" || selectedType() == "Catalog Radio" ) {
updateToLabelAndCombo();
} else if( selectedType() == "Sorting" ) {
QComboBox* match = qobject_cast<QComboBox*>( m_match.data() );

View File

@@ -21,6 +21,9 @@
#include "dynamic/echonest/EchonestSteerer.h"
#include "query.h"
#include "utils/tomahawkutils.h"
#include "tomahawksettings.h"
#include "database/databasecommand_collectionattributes.h"
#include "database/database.h"
#include "utils/logger.h"
#include <QFile>
#include <QDir>
@@ -33,6 +36,9 @@ 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 >();
EchonestFactory::EchonestFactory()
{
@@ -56,9 +62,16 @@ EchonestFactory::createControl( const QString& controlType )
QStringList
EchonestFactory::typeSelectors() const
{
return QStringList() << "Artist" << "Artist Description" << "Song" << "Mood" << "Style" << "Variety" << "Tempo" << "Duration" << "Loudness"
QStringList types = QStringList() << "Artist" << "Artist Description" << "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;
}
@@ -72,6 +85,17 @@ 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;
}
}
// qDebug() << "ECHONEST:" << m_logo.size();
}
@@ -356,6 +380,27 @@ EchonestGenerator::resetSteering()
m_steerData.second = QString();
}
void
EchonestGenerator::collectionAttributes(PairList data)
{
QPair<QString, QString> part;
foreach ( part, data )
{
s_catalogs.insert( part.first, part.second );
}
}
QByteArray
EchonestGenerator::catalogId(const QString &collectionId)
{
return s_catalogs.value( collectionId ).toUtf8();
}
QStringList
EchonestGenerator::userCatalogs()
{
return s_catalogs.keys();
}
bool
EchonestGenerator::onlyThisArtistType( Echonest::DynamicPlaylist::ArtistTypeEnum type ) const throw( std::runtime_error )
@@ -389,12 +434,17 @@ EchonestGenerator::appendRadioType( Echonest::DynamicPlaylist::PlaylistParams& p
*
*/
/// 1. artist: If all the artist controls are Limit-To. If some were but not all, error out.
/// 2. artist-description: If all the artist entries are Description. If some were but not all, error out.
/// 3. artist-radio: If all the artist entries are Similar To. If some were but not all, error out.
/// 4. song-radio: If all the artist entries are Similar To. If some were but not all, error out.
if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistType ) )
params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistType ) );
/// 1. catalog-radio: If any the entries are catalog types.
/// 2. artist: If all the artist controls are Limit-To. If some were but not all, error out.
/// 3. artist-description: If all the artist entries are Description. If some were but not all, error out.
/// 4. artist-radio: If all the artist entries are Similar To. If some were but not all, error out.
/// 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 ) {
someCatalog = true;
}
if( someCatalog )
params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::CatalogRadioType ) );
else if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistDescriptionType ) )
params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) );
else if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistRadioType ) )

View File

@@ -25,6 +25,7 @@
#include "playlist/dynamic/GeneratorInterface.h"
#include "playlist/dynamic/GeneratorFactory.h"
#include "playlist/dynamic/DynamicControl.h"
#include "database/databasecommand_collectionattributes.h"
#include "dllmacro.h"
@@ -61,6 +62,8 @@ public:
static QStringList styles();
static QStringList moods();
static QStringList userCatalogs();
static QByteArray catalogId( const QString& collectionId );
signals:
void paramsGenerated( const Echonest::DynamicPlaylist::PlaylistParams& );
@@ -80,6 +83,7 @@ private slots:
void stylesReceived();
void moodsReceived();
void collectionAttributes(PairList);
void songLookupFinished();
private:
@@ -101,6 +105,9 @@ private:
static QNetworkReply* s_stylesJob;
static QNetworkReply* s_moodsJob;
static bool s_catalogsFetched;
static QHash< QString, QString > s_catalogs;
// used for the intermediary song id lookup
QSet< QNetworkReply* > m_waiting;
Echonest::DynamicPlaylist::PlaylistParams m_storedParams;

View File

@@ -473,6 +473,20 @@ TomahawkSettings::setShowOfflineSources( bool show )
setValue( "collection/sources/showoffline", show );
}
bool
TomahawkSettings::enableEchonestCatalogs() const
{
return value( "collection/enable_catalogs", false ).toBool();
}
void
TomahawkSettings::setEnableEchonestCatalogs( bool enable )
{
setValue( "collection/enable_catalogs", enable );
emit changed();
}
QByteArray
TomahawkSettings::playlistColumnSizes( const QString& playlistid ) const
{

View File

@@ -76,6 +76,9 @@ public:
bool showOfflineSources() const;
void setShowOfflineSources( bool show );
bool enableEchonestCatalogs() const;
void setEnableEchonestCatalogs( bool enable );
/// Playlist stuff
QByteArray playlistColumnSizes( const QString& playlistid ) const;
void setPlaylistColumnSizes( const QString& playlistid, const QByteArray& state );

View File

@@ -136,6 +136,8 @@ SettingsDialog::SettingsDialog( QWidget *parent )
ui->checkBoxWatchForChanges->setChecked( s->watchForChanges() );
ui->scannerTimeSpinBox->setValue( s->scannerTime() );
ui->enableEchonestCatalog->setChecked( s->enableEchonestCatalogs() );
connect( ui->checkBoxWatchForChanges, SIGNAL( clicked( bool ) ), SLOT( updateScanOptionsView() ) );
connect( ui->scannerDirModeButton, SIGNAL( clicked( bool ) ), SLOT( updateScanOptionsView() ) );
connect( ui->scannerFileModeButton, SIGNAL( clicked( bool ) ), SLOT( updateScanOptionsView() ) );
@@ -239,6 +241,7 @@ SettingsDialog::~SettingsDialog()
s->setWatchForChanges( ui->checkBoxWatchForChanges->isChecked() );
s->setScannerTime( ui->scannerTimeSpinBox->value() );
s->setScannerMode( ui->scannerFileModeButton->isChecked() ? TomahawkSettings::Files : TomahawkSettings::Dirs );
s->setEnableEchonestCatalogs( ui->enableEchonestCatalog->isChecked() );
s->setNowPlayingEnabled( ui->checkBoxEnableAdium->isChecked() );
@@ -510,7 +513,6 @@ SettingsDialog::onLastFmFinished()
#endif
}
void
SettingsDialog::addScriptResolver()
{

View File

@@ -66,9 +66,6 @@ public:
explicit SettingsDialog( QWidget* parent = 0 );
~SettingsDialog();
signals:
void settingsChanged();
protected:
void changeEvent( QEvent* e );

View File

@@ -211,6 +211,13 @@
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_16">
<item>
<widget class="QCheckBox" name="enableEchonestCatalog">
<property name="text">
<string>Upload catalog to The Echo Nest to enable recommended radios.</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxWatchForChanges">
<property name="sizePolicy">

View File

@@ -56,6 +56,7 @@
#include "pipeline.h"
#include "utils/spotifyparser.h"
#include "dropjob.h"
#include "EchonestCatalogSynchronizer.h"
#include "audio/audioengine.h"
#include "utils/xspfloader.h"
@@ -264,6 +265,8 @@ TomahawkApp::init()
// Make sure to init GAM in the gui thread
GlobalActionManager::instance();
// Set up echonest catalog synchronizer
Tomahawk::EchonestCatalogSynchronizer::instance();
// check if our spotify playlist api server is up and running, and enable spotify playlist drops if so
QNetworkReply* r = TomahawkUtils::nam()->get( QNetworkRequest( QUrl( SPOTIFY_PLAYLIST_API_URL "/playlist/test" ) ) );
connect( r, SIGNAL( finished() ), this, SLOT( spotifyApiCheckFinished() ) );