mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-03-23 01:09:42 +01:00
Merge branch 'dragndrop'
This commit is contained in:
commit
98030c9b05
@ -167,6 +167,7 @@ set( libSources
|
||||
utils/xspfloader.cpp
|
||||
utils/xspfgenerator.cpp
|
||||
utils/jspfloader.cpp
|
||||
utils/spotifyparser.cpp
|
||||
|
||||
widgets/newplaylistwidget.cpp
|
||||
widgets/searchwidget.cpp
|
||||
@ -338,6 +339,7 @@ set( libHeaders
|
||||
utils/xspfloader.h
|
||||
utils/xspfgenerator.h
|
||||
utils/jspfloader.h
|
||||
utils/spotifyparser.h
|
||||
|
||||
widgets/newplaylistwidget.h
|
||||
widgets/searchwidget.h
|
||||
@ -427,7 +429,7 @@ IF( APPLE )
|
||||
widgets/maclineedit.mm
|
||||
utils/tomahawkutils_mac.mm )
|
||||
|
||||
SET( libHeaders ${libHeaders}
|
||||
SET( libHeaders ${libHeaders}
|
||||
infosystem/infoplugins/mac/adiumplugin.h
|
||||
widgets/maclineedit.h )
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <QClipboard>
|
||||
#include <QUrl>
|
||||
|
||||
#include "artist.h"
|
||||
#include "album.h"
|
||||
#include "sourcelist.h"
|
||||
#include "pipeline.h"
|
||||
@ -42,9 +43,13 @@
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include "utils/jspfloader.h"
|
||||
#include <QMimeData>
|
||||
#include "utils/spotifyparser.h"
|
||||
|
||||
GlobalActionManager* GlobalActionManager::s_instance = 0;
|
||||
|
||||
using namespace Tomahawk;
|
||||
|
||||
GlobalActionManager*
|
||||
GlobalActionManager::instance()
|
||||
{
|
||||
@ -54,18 +59,17 @@ GlobalActionManager::instance()
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
|
||||
GlobalActionManager::GlobalActionManager( QObject* parent )
|
||||
: QObject( parent )
|
||||
{
|
||||
|
||||
m_mimeTypes << "application/tomahawk.query.list" << "application/tomahawk.plentry.list" << "application/tomahawk.result.list" << "text/plain";
|
||||
}
|
||||
|
||||
GlobalActionManager::~GlobalActionManager()
|
||||
{}
|
||||
|
||||
QUrl
|
||||
GlobalActionManager::openLinkFromQuery( const Tomahawk::query_ptr& query ) const
|
||||
GlobalActionManager::openLinkFromQuery( const query_ptr& query ) const
|
||||
{
|
||||
QString title, artist, album;
|
||||
|
||||
@ -100,9 +104,9 @@ GlobalActionManager::openLink( const QString& title, const QString& artist, cons
|
||||
}
|
||||
|
||||
QString
|
||||
GlobalActionManager::copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& playlist )
|
||||
GlobalActionManager::copyPlaylistToClipboard( const dynplaylist_ptr& playlist )
|
||||
{
|
||||
QUrl link( QString( "%1/%2/create/" ).arg( hostname() ).arg( playlist->mode() == Tomahawk::OnDemand ? "station" : "autoplaylist" ) );
|
||||
QUrl link( QString( "%1/%2/create/" ).arg( hostname() ).arg( playlist->mode() == OnDemand ? "station" : "autoplaylist" ) );
|
||||
|
||||
if( playlist->generator()->type() != "echonest" ) {
|
||||
tLog() << "Only echonest generators are supported";
|
||||
@ -112,8 +116,8 @@ GlobalActionManager::copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& p
|
||||
link.addEncodedQueryItem( "type", "echonest" );
|
||||
link.addQueryItem( "title", playlist->title() );
|
||||
|
||||
QList< Tomahawk::dyncontrol_ptr > controls = playlist->generator()->controls();
|
||||
foreach( const Tomahawk::dyncontrol_ptr& c, controls ) {
|
||||
QList< dyncontrol_ptr > controls = playlist->generator()->controls();
|
||||
foreach( const dyncontrol_ptr& c, controls ) {
|
||||
if( c->selectedType() == "Artist" ) {
|
||||
if( c->match().toInt() == Echonest::DynamicPlaylist::ArtistType )
|
||||
link.addQueryItem( "artist_limitto", c->input() );
|
||||
@ -142,7 +146,7 @@ GlobalActionManager::copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& p
|
||||
}
|
||||
|
||||
void
|
||||
GlobalActionManager::savePlaylistToFile( const Tomahawk::playlist_ptr& playlist, const QString& filename )
|
||||
GlobalActionManager::savePlaylistToFile( const playlist_ptr& playlist, const QString& filename )
|
||||
{
|
||||
XSPFGenerator* g = new XSPFGenerator( playlist, this );
|
||||
g->setProperty( "filename", filename );
|
||||
@ -169,7 +173,7 @@ GlobalActionManager::xspfCreated( const QByteArray& xspf )
|
||||
|
||||
|
||||
void
|
||||
GlobalActionManager::copyToClipboard( const Tomahawk::query_ptr& query ) const
|
||||
GlobalActionManager::copyToClipboard( const query_ptr& query ) const
|
||||
{
|
||||
QClipboard* cb = QApplication::clipboard();
|
||||
cb->setText( openLinkFromQuery( query ).toEncoded() );
|
||||
@ -199,7 +203,7 @@ GlobalActionManager::parseTomahawkLink( const QString& url )
|
||||
return true;
|
||||
} else if( u.hasQueryItem( "jspf" ) ) {
|
||||
QUrl jspf = QUrl::fromUserInput( u.queryItemValue( "jspf" ) );
|
||||
Tomahawk::JSPFLoader* l = new Tomahawk::JSPFLoader( true, this );
|
||||
JSPFLoader* l = new JSPFLoader( true, this );
|
||||
tDebug() << "Loading jspiff:" << jspf.toString();
|
||||
l->load( jspf );
|
||||
connect( l, SIGNAL( ok( Tomahawk::playlist_ptr ) ), ViewManager::instance(), SLOT( show( Tomahawk::playlist_ptr ) ) );
|
||||
@ -262,7 +266,7 @@ GlobalActionManager::handlePlaylistCommand( const QUrl& url )
|
||||
tLog() << "New playlist command needs a title...";
|
||||
return false;
|
||||
}
|
||||
Tomahawk::playlist_ptr pl = Tomahawk::Playlist::create( SourceList::instance()->getLocal(), uuid(), url.queryItemValue( "title" ), QString(), QString(), false );
|
||||
playlist_ptr pl = Playlist::create( SourceList::instance()->getLocal(), uuid(), url.queryItemValue( "title" ), QString(), QString(), false );
|
||||
ViewManager::instance()->show( pl );
|
||||
} else if( parts[ 0 ] == "add" ) {
|
||||
if( !url.hasQueryItem( "playlistid" ) || !url.hasQueryItem( "title" ) || !url.hasQueryItem( "artist" ) ) {
|
||||
@ -305,7 +309,7 @@ GlobalActionManager::handleOpenCommand(const QUrl& url)
|
||||
}
|
||||
|
||||
void
|
||||
GlobalActionManager::handleOpenTrack ( const Tomahawk::query_ptr& q )
|
||||
GlobalActionManager::handleOpenTrack ( const query_ptr& q )
|
||||
{
|
||||
ViewManager::instance()->queue()->model()->append( q );
|
||||
ViewManager::instance()->showQueue();
|
||||
@ -355,10 +359,10 @@ GlobalActionManager::doQueueAdd( const QStringList& parts, const QList< QPair< Q
|
||||
}
|
||||
|
||||
if( !title.isEmpty() || !artist.isEmpty() || !album.isEmpty() ) { // an individual; query to add to queue
|
||||
Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album, uuid(), false );
|
||||
query_ptr q = Query::get( artist, title, album, uuid(), false );
|
||||
if( !urlStr.isEmpty() )
|
||||
q->setResultHint( urlStr );
|
||||
Tomahawk::Pipeline::instance()->resolve( q, true );
|
||||
Pipeline::instance()->resolve( q, true );
|
||||
|
||||
handleOpenTrack( q );
|
||||
return true;
|
||||
@ -373,10 +377,10 @@ GlobalActionManager::doQueueAdd( const QStringList& parts, const QList< QPair< Q
|
||||
// TODO
|
||||
} else { // give it a web result hint
|
||||
QFileInfo info( track.path() );
|
||||
Tomahawk::query_ptr q = Tomahawk::Query::get( QString(), info.baseName(), QString(), uuid(), false );
|
||||
query_ptr q = Query::get( QString(), info.baseName(), QString(), uuid(), false );
|
||||
q->setResultHint( track.toString() );
|
||||
|
||||
Tomahawk::Pipeline::instance()->resolve( q, true );
|
||||
Pipeline::instance()->resolve( q, true );
|
||||
|
||||
ViewManager::instance()->queue()->model()->append( q );
|
||||
ViewManager::instance()->showQueue();
|
||||
@ -415,139 +419,139 @@ GlobalActionManager::handleAutoPlaylistCommand( const QUrl& url )
|
||||
return !loadDynamicPlaylist( url, false ).isNull();
|
||||
}
|
||||
|
||||
Tomahawk::dynplaylist_ptr
|
||||
dynplaylist_ptr
|
||||
GlobalActionManager::loadDynamicPlaylist( const QUrl& url, bool station )
|
||||
{
|
||||
QStringList parts = url.path().split( "/" ).mid( 1 ); // get the rest of the command
|
||||
if( parts.isEmpty() ) {
|
||||
tLog() << "No specific station command:" << url.toString();
|
||||
return Tomahawk::dynplaylist_ptr();
|
||||
return dynplaylist_ptr();
|
||||
}
|
||||
|
||||
if( parts[ 0 ] == "create" ) {
|
||||
if( !url.hasQueryItem( "title" ) || !url.hasQueryItem( "type" ) ) {
|
||||
tLog() << "Station create command needs title and type..." << url.toString();
|
||||
return Tomahawk::dynplaylist_ptr();
|
||||
return dynplaylist_ptr();
|
||||
}
|
||||
QString title = url.queryItemValue( "title" );
|
||||
QString type = url.queryItemValue( "type" );
|
||||
Tomahawk::GeneratorMode m = Tomahawk::Static;
|
||||
GeneratorMode m = Static;
|
||||
if( station )
|
||||
m = Tomahawk::OnDemand;
|
||||
m = OnDemand;
|
||||
|
||||
Tomahawk::dynplaylist_ptr pl = Tomahawk::DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), title, QString(), QString(), m, false, type );
|
||||
dynplaylist_ptr pl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), title, QString(), QString(), m, false, type );
|
||||
pl->setMode( m );
|
||||
QList< Tomahawk::dyncontrol_ptr > controls;
|
||||
QList< dyncontrol_ptr > controls;
|
||||
QPair< QString, QString > param;
|
||||
foreach( param, url.queryItems() ) {
|
||||
if( param.first == "artist" ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Artist" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Artist" );
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistRadioType ) );
|
||||
controls << c;
|
||||
} else if( param.first == "artist_limitto" ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Artist" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Artist" );
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistType ) );
|
||||
controls << c;
|
||||
} else if( param.first == "description" ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Artist Description" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Artist Description" );
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistDescriptionType ) );
|
||||
controls << c;
|
||||
} else if( param.first == "variety" ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Variety" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Variety" );
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Variety ) );
|
||||
controls << c;
|
||||
} else if( param.first.startsWith( "tempo" ) ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Tempo" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Tempo" );
|
||||
int extra = param.first.endsWith( "_max" ) ? -1 : 0;
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinTempo + extra ) );
|
||||
controls << c;
|
||||
} else if( param.first.startsWith( "duration" ) ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Duration" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Duration" );
|
||||
int extra = param.first.endsWith( "_max" ) ? -1 : 0;
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinDuration + extra ) );
|
||||
controls << c;
|
||||
} else if( param.first.startsWith( "loudness" ) ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Loudness" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Loudness" );
|
||||
int extra = param.first.endsWith( "_max" ) ? -1 : 0;
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinLoudness + extra ) );
|
||||
controls << c;
|
||||
} else if( param.first.startsWith( "danceability" ) ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Danceability" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Danceability" );
|
||||
int extra = param.first.endsWith( "_max" ) ? 1 : 0;
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinDanceability + extra ) );
|
||||
controls << c;
|
||||
} else if( param.first.startsWith( "energy" ) ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Energy" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Energy" );
|
||||
int extra = param.first.endsWith( "_max" ) ? 1 : 0;
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinEnergy + extra ) );
|
||||
controls << c;
|
||||
} else if( param.first.startsWith( "artist_familiarity" ) ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Artist Familiarity" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Artist Familiarity" );
|
||||
int extra = param.first.endsWith( "_max" ) ? -1 : 0;
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinFamiliarity + extra ) );
|
||||
controls << c;
|
||||
} else if( param.first.startsWith( "artist_hotttnesss" ) ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Artist Hotttnesss" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Artist Hotttnesss" );
|
||||
int extra = param.first.endsWith( "_max" ) ? -1 : 0;
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinHotttnesss + extra ) );
|
||||
controls << c;
|
||||
} else if( param.first.startsWith( "song_hotttnesss" ) ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Song Hotttnesss" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Song Hotttnesss" );
|
||||
int extra = param.first.endsWith( "_max" ) ? -1 : 0;
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::SongMinHotttnesss + extra ) );
|
||||
controls << c;
|
||||
} else if( param.first.startsWith( "longitude" ) ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Longitude" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Longitude" );
|
||||
int extra = param.first.endsWith( "_max" ) ? 1 : 0;
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinLongitude + extra ) );
|
||||
controls << c;
|
||||
} else if( param.first.startsWith( "latitude" ) ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Latitude" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Latitude" );
|
||||
int extra = param.first.endsWith( "_max" ) ? 1 : 0;
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinLatitude + extra ) );
|
||||
controls << c;
|
||||
} else if( param.first == "key" ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Key" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Key" );
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Key ) );
|
||||
controls << c;
|
||||
} else if( param.first == "mode" ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Mode" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Mode" );
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Mode ) );
|
||||
controls << c;
|
||||
} else if( param.first == "mood" ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Mood" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Mood" );
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Mood ) );
|
||||
controls << c;
|
||||
} else if( param.first == "style" ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Style" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Style" );
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Style ) );
|
||||
controls << c;
|
||||
} else if( param.first == "song" ) {
|
||||
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Song" );
|
||||
dyncontrol_ptr c = pl->generator()->createControl( "Song" );
|
||||
c->setInput( param.second );
|
||||
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::SongRadioType ) );
|
||||
controls << c;
|
||||
}
|
||||
}
|
||||
if( m == Tomahawk::OnDemand )
|
||||
if( m == OnDemand )
|
||||
pl->createNewRevision( uuid(), pl->currentrevision(), type, controls );
|
||||
else
|
||||
pl->createNewRevision( uuid(), pl->currentrevision(), type, controls, pl->entries() );
|
||||
@ -555,7 +559,7 @@ GlobalActionManager::loadDynamicPlaylist( const QUrl& url, bool station )
|
||||
return pl;
|
||||
}
|
||||
|
||||
return Tomahawk::dynplaylist_ptr();
|
||||
return dynplaylist_ptr();
|
||||
}
|
||||
|
||||
|
||||
@ -587,10 +591,10 @@ GlobalActionManager::handlePlayCommand( const QUrl& url )
|
||||
else if( pair.first == "url" )
|
||||
urlStr = pair.second;
|
||||
}
|
||||
Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album );
|
||||
query_ptr q = Query::get( artist, title, album );
|
||||
if( !urlStr.isEmpty() )
|
||||
q->setResultHint( urlStr );
|
||||
Tomahawk::Pipeline::instance()->resolve( q, true );
|
||||
Pipeline::instance()->resolve( q, true );
|
||||
|
||||
m_waitingToPlay = q;
|
||||
connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( waitingForResolved( bool ) ) );
|
||||
@ -622,14 +626,14 @@ bool GlobalActionManager::handleBookmarkCommand(const QUrl& url)
|
||||
else if( pair.first == "url" )
|
||||
urlStr = pair.second;
|
||||
}
|
||||
Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album );
|
||||
query_ptr q = Query::get( artist, title, album );
|
||||
if( !urlStr.isEmpty() )
|
||||
q->setResultHint( urlStr );
|
||||
Tomahawk::Pipeline::instance()->resolve( q, true );
|
||||
Pipeline::instance()->resolve( q, true );
|
||||
|
||||
// now we add it to the special "bookmarks" playlist, creating it if it doesn't exist. if nothing is playing, start playing the track
|
||||
QSharedPointer< LocalCollection > col = SourceList::instance()->getLocal()->collection().dynamicCast< LocalCollection >();
|
||||
Tomahawk::playlist_ptr bookmarkpl = col->bookmarksPlaylist();
|
||||
playlist_ptr bookmarkpl = col->bookmarksPlaylist();
|
||||
if( bookmarkpl.isNull() ) { // create it and do the deed then
|
||||
m_waitingToBookmark = q;
|
||||
col->createBookmarksPlaylist();
|
||||
@ -647,16 +651,16 @@ bool GlobalActionManager::handleBookmarkCommand(const QUrl& url)
|
||||
|
||||
|
||||
void
|
||||
GlobalActionManager::bookmarkPlaylistCreated( const Tomahawk::playlist_ptr& pl )
|
||||
GlobalActionManager::bookmarkPlaylistCreated( const playlist_ptr& pl )
|
||||
{
|
||||
Q_ASSERT( !m_waitingToBookmark.isNull() );
|
||||
doBookmark( pl, m_waitingToBookmark );
|
||||
}
|
||||
|
||||
void
|
||||
GlobalActionManager::doBookmark( const Tomahawk::playlist_ptr& pl, const Tomahawk::query_ptr& q )
|
||||
GlobalActionManager::doBookmark( const playlist_ptr& pl, const query_ptr& q )
|
||||
{
|
||||
Tomahawk::plentry_ptr e( new Tomahawk::PlaylistEntry );
|
||||
plentry_ptr e( new PlaylistEntry );
|
||||
e->setGuid( uuid() );
|
||||
|
||||
if ( q->results().count() )
|
||||
@ -668,7 +672,7 @@ GlobalActionManager::doBookmark( const Tomahawk::playlist_ptr& pl, const Tomahaw
|
||||
e->setAnnotation( "" ); // FIXME
|
||||
e->setQuery( q );
|
||||
|
||||
pl->createNewRevision( uuid(), pl->currentrevision(), QList< Tomahawk::plentry_ptr >( pl->entries() ) << e );
|
||||
pl->createNewRevision( uuid(), pl->currentrevision(), QList< plentry_ptr >( pl->entries() ) << e );
|
||||
connect( pl.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( showPlaylist() ) );
|
||||
|
||||
m_toShow = pl;
|
||||
@ -712,82 +716,111 @@ GlobalActionManager::hostname() const
|
||||
return QString( "http://toma.hk" );
|
||||
}
|
||||
|
||||
/// SPOTIFY URL HANDLING
|
||||
/// QMIMEDATA HANDLING
|
||||
|
||||
bool
|
||||
GlobalActionManager::parseSpotifyLink( const QString& link )
|
||||
QStringList
|
||||
GlobalActionManager::mimeTypes() const
|
||||
{
|
||||
if( !link.contains( "track" ) ) // we only support track links atm
|
||||
return false;
|
||||
|
||||
// we need Spotify URIs such as spotify:track:XXXXXX, so if we by chance get a http://open.spotify.com url, convert it
|
||||
QString uri = link;
|
||||
if( link.contains( "open.spotify.com" ) )
|
||||
{
|
||||
QString hash = link;
|
||||
hash.replace( "http://open.spotify.com/track/", "" );
|
||||
uri = QString( "spotify:track:%1" ).arg( hash );
|
||||
}
|
||||
|
||||
tLog() << "Parsing Spotify Track URI:" << uri;
|
||||
|
||||
QUrl url = QUrl( QString( "http://ws.spotify.com/lookup/1/.json?uri=%1" ).arg( uri ) );
|
||||
tDebug() << "Looking up..." << url.toString();
|
||||
|
||||
QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
|
||||
connect( reply, SIGNAL( finished() ), this, SLOT( spotifyTrackLookupFinished() ) );
|
||||
|
||||
return true; // all we know now
|
||||
return m_mimeTypes;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
GlobalActionManager::acceptsMimeData( const QMimeData* data )
|
||||
{
|
||||
if ( data->hasFormat( "application/tomahawk.query.list" )
|
||||
|| data->hasFormat( "application/tomahawk.plentry.list" )
|
||||
|| data->hasFormat( "application/tomahawk.result.list" ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// crude check for spotify data
|
||||
if ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "spotify" ) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GlobalActionManager::spotifyTrackLookupFinished()
|
||||
GlobalActionManager::tracksFromMimeData( const QMimeData* data )
|
||||
{
|
||||
QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() );
|
||||
Q_ASSERT( r );
|
||||
|
||||
if( r->error() == QNetworkReply::NoError )
|
||||
if ( data->hasFormat( "application/tomahawk.query.list" ) )
|
||||
emit tracks( tracksFromQueryList( data ) );
|
||||
else if ( data->hasFormat( "application/tomahawk.result.list" ) )
|
||||
emit tracks( tracksFromResultList( data ) );
|
||||
else if ( data->hasFormat( "text/plain" ) )
|
||||
{
|
||||
QJson::Parser p;
|
||||
bool ok;
|
||||
QVariantMap res = p.parse( r, &ok ).toMap();
|
||||
QString plainData = QString::fromUtf8( data->data( "text/plain" ).constData() );
|
||||
tDebug() << "Got text/plain mime data:" << data->data( "text/plain" ) << "decoded to:" << plainData;
|
||||
if( plainData.contains( "open.spotify.com") ||
|
||||
plainData.contains( "spotify:track" ) )
|
||||
{
|
||||
QStringList tracks = plainData.split( "\n" );
|
||||
|
||||
if( !ok )
|
||||
{
|
||||
tLog() << "Failed to parse json from Spotify track lookup:" << p.errorString() << "On line" << p.errorLine();
|
||||
return;
|
||||
} else if( !res.contains( "track" ) )
|
||||
{
|
||||
tLog() << "No 'track' item in the spotify track lookup result... not doing anything";
|
||||
return;
|
||||
tDebug() << "Got a list of spotify urls!" << tracks;
|
||||
SpotifyParser* spot = new SpotifyParser( tracks, this );
|
||||
connect( spot, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ) );
|
||||
}
|
||||
|
||||
// lets parse this baby
|
||||
QVariantMap t = res.value( "track" ).toMap();
|
||||
|
||||
QString title, artist, album;
|
||||
|
||||
title = t.value( "name", QString() ).toString();
|
||||
// TODO for now only take the first artist
|
||||
if( t.contains( "artists" ) && t[ "artists" ].canConvert< QVariantList >() && t[ "artists" ].toList().size() > 0 )
|
||||
artist = t[ "artists" ].toList().first().toMap().value( "name", QString() ).toString();
|
||||
if( t.contains( "album" ) && t[ "album" ].canConvert< QVariantMap >() )
|
||||
album = t[ "album" ].toMap().value( "name", QString() ).toString();
|
||||
|
||||
if( title.isEmpty() && artist.isEmpty() ) // don't have enough...
|
||||
{
|
||||
tLog() << "Didn't get an artist and track name from spotify, not enough to build a query on. Aborting" << title << artist << album;
|
||||
return;
|
||||
}
|
||||
|
||||
Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album, uuid(), true );
|
||||
handleOpenTrack( q );
|
||||
|
||||
} else
|
||||
{
|
||||
tLog() << "Error in network request to Spotify for track decoding:" << r->errorString();
|
||||
}
|
||||
}
|
||||
|
||||
QList< query_ptr >
|
||||
GlobalActionManager::tracksFromQueryList( const QMimeData* data )
|
||||
{
|
||||
QList< query_ptr > queries;
|
||||
QByteArray itemData = data->data( "application/tomahawk.query.list" );
|
||||
QDataStream stream( &itemData, QIODevice::ReadOnly );
|
||||
|
||||
while ( !stream.atEnd() )
|
||||
{
|
||||
qlonglong qptr;
|
||||
stream >> qptr;
|
||||
|
||||
query_ptr* query = reinterpret_cast<query_ptr*>(qptr);
|
||||
if ( query && !query->isNull() )
|
||||
{
|
||||
tDebug() << "Dropped query item:" << query->data()->artist() << "-" << query->data()->track();
|
||||
queries << *query;
|
||||
}
|
||||
}
|
||||
|
||||
return queries;
|
||||
}
|
||||
|
||||
QList< query_ptr >
|
||||
GlobalActionManager::tracksFromResultList( const QMimeData* data )
|
||||
{
|
||||
QList< query_ptr > queries;
|
||||
QByteArray itemData = data->data( "application/tomahawk.result.list" );
|
||||
QDataStream stream( &itemData, QIODevice::ReadOnly );
|
||||
|
||||
while ( !stream.atEnd() )
|
||||
{
|
||||
qlonglong qptr;
|
||||
stream >> qptr;
|
||||
|
||||
result_ptr* result = reinterpret_cast<result_ptr*>(qptr);
|
||||
if ( result && !result->isNull() )
|
||||
{
|
||||
tDebug() << "Dropped result item:" << result->data()->artist()->name() << "-" << result->data()->track();
|
||||
query_ptr q = result->data()->toQuery();
|
||||
q->addResults( QList< result_ptr >() << *result );
|
||||
queries << q;
|
||||
}
|
||||
}
|
||||
|
||||
return queries;
|
||||
}
|
||||
|
||||
/// SPOTIFY URL HANDLING
|
||||
|
||||
bool
|
||||
GlobalActionManager::openSpotifyLink( const QString& link )
|
||||
{
|
||||
SpotifyParser* spot = new SpotifyParser( link, this );
|
||||
connect( spot, SIGNAL( track( Tomahawk::query_ptr ) ), this, SLOT( handleOpenTrack( Tomahawk::query_ptr ) ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -28,6 +28,9 @@
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
|
||||
/**
|
||||
* Handles global actions such as parsing and creation of links, mime data handling, etc
|
||||
*/
|
||||
class DLLEXPORT GlobalActionManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -38,32 +41,47 @@ public:
|
||||
QUrl openLinkFromQuery( const Tomahawk::query_ptr& query ) const;
|
||||
QUrl openLink( const QString& title, const QString& artist, const QString& album ) const;
|
||||
|
||||
// spotify
|
||||
bool parseSpotifyLink( const QString& link );
|
||||
/// Takes a spotify link and performs the default open action on it
|
||||
bool openSpotifyLink( const QString& link );
|
||||
|
||||
void copyToClipboard( const Tomahawk::query_ptr& query ) const;
|
||||
QString copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& playlist );
|
||||
void savePlaylistToFile( const Tomahawk::playlist_ptr& playlist, const QString& filename );
|
||||
|
||||
/**
|
||||
* QMimeData helpers
|
||||
*
|
||||
* Call this to parse the tracks in a QMimeData object to query_ptrs. This will parse internal tomahawk
|
||||
* data as well as all other formats supported (spotify, etc).
|
||||
*
|
||||
* Connect to tracks( QList< query_ptr> ); for the extracted tracks.
|
||||
*/
|
||||
bool acceptsMimeData( const QMimeData* data );
|
||||
void tracksFromMimeData( const QMimeData* data );
|
||||
QStringList mimeTypes() const;
|
||||
|
||||
public slots:
|
||||
bool parseTomahawkLink( const QString& link );
|
||||
void waitingForResolved( bool );
|
||||
|
||||
Tomahawk::dynplaylist_ptr loadDynamicPlaylist( const QUrl& url, bool station );
|
||||
|
||||
signals:
|
||||
/// QMimeData parsing results
|
||||
void tracks( const QList< Tomahawk::query_ptr >& tracks );
|
||||
|
||||
private slots:
|
||||
void bookmarkPlaylistCreated( const Tomahawk::playlist_ptr& pl );
|
||||
void showPlaylist();
|
||||
|
||||
void xspfCreated( const QByteArray& xspf );
|
||||
|
||||
// SPOTIFY
|
||||
void spotifyTrackLookupFinished();
|
||||
|
||||
void handleOpenTrack( const Tomahawk::query_ptr& qry );
|
||||
private:
|
||||
explicit GlobalActionManager( QObject* parent = 0 );
|
||||
void doBookmark( const Tomahawk::playlist_ptr& pl, const Tomahawk::query_ptr& q );
|
||||
|
||||
/// handle opening of urls
|
||||
bool handlePlaylistCommand( const QUrl& url );
|
||||
bool handleCollectionCommand(const QUrl& url );
|
||||
bool handleQueueCommand(const QUrl& url );
|
||||
@ -73,16 +91,19 @@ private:
|
||||
bool handlePlayCommand(const QUrl& url );
|
||||
bool handleBookmarkCommand(const QUrl& url );
|
||||
bool handleOpenCommand(const QUrl& url );
|
||||
|
||||
void handleOpenTrack( const Tomahawk::query_ptr& qry );
|
||||
|
||||
bool doQueueAdd( const QStringList& parts, const QList< QPair< QString, QString > >& queryItems );
|
||||
|
||||
/// handle parsing mime data
|
||||
QList< Tomahawk::query_ptr > tracksFromQueryList( const QMimeData* d );
|
||||
QList< Tomahawk::query_ptr > tracksFromResultList( const QMimeData* d );
|
||||
|
||||
QString hostname() const;
|
||||
|
||||
Tomahawk::playlist_ptr m_toShow;
|
||||
Tomahawk::query_ptr m_waitingToBookmark;
|
||||
Tomahawk::query_ptr m_waitingToPlay;
|
||||
|
||||
QStringList m_mimeTypes;
|
||||
static GlobalActionManager* s_instance;
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@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
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@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
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "database/databasecommand_playbackhistory.h"
|
||||
#include "dynamic/GeneratorInterface.h"
|
||||
#include "utils/logger.h"
|
||||
#include "globalactionmanager.h"
|
||||
|
||||
using namespace Tomahawk;
|
||||
|
||||
@ -38,6 +39,9 @@ PlaylistModel::PlaylistModel( QObject* parent )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
m_dropStorage.parent = QPersistentModelIndex();
|
||||
m_dropStorage.row = -10;
|
||||
|
||||
setReadOnly( false );
|
||||
}
|
||||
|
||||
@ -168,7 +172,7 @@ PlaylistModel::clear()
|
||||
{
|
||||
if ( rowCount( QModelIndex() ) )
|
||||
{
|
||||
emit loadingFinished();;
|
||||
emit loadingFinished();
|
||||
|
||||
emit beginResetModel();
|
||||
delete m_rootItem;
|
||||
@ -355,16 +359,32 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r
|
||||
if ( action == Qt::IgnoreAction || isReadOnly() )
|
||||
return true;
|
||||
|
||||
if ( !data->hasFormat( "application/tomahawk.query.list" )
|
||||
&& !data->hasFormat( "application/tomahawk.plentry.list" )
|
||||
&& !data->hasFormat( "application/tomahawk.result.list" ) )
|
||||
if ( !GlobalActionManager::instance()->acceptsMimeData( data ) )
|
||||
return false;
|
||||
|
||||
m_dropStorage.row = row;
|
||||
m_dropStorage.parent = QPersistentModelIndex( parent );
|
||||
m_dropStorage.action = action;
|
||||
connect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
|
||||
GlobalActionManager::instance()->tracksFromMimeData( data );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PlaylistModel::parsedDroppedTracks( QList< query_ptr > tracks )
|
||||
{
|
||||
|
||||
if ( m_dropStorage.row == -10 ) // nope
|
||||
return;
|
||||
|
||||
disconnect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
|
||||
|
||||
int beginRow;
|
||||
if ( row != -1 )
|
||||
beginRow = row;
|
||||
else if ( parent.isValid() )
|
||||
beginRow = parent.row();
|
||||
if ( m_dropStorage.row != -1 )
|
||||
beginRow = m_dropStorage.row;
|
||||
else if ( m_dropStorage.parent.isValid() )
|
||||
beginRow = m_dropStorage.parent.row();
|
||||
else
|
||||
beginRow = rowCount( QModelIndex() );
|
||||
|
||||
@ -373,51 +393,10 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r
|
||||
if ( currentItem().isValid() )
|
||||
currentuuid = itemFromIndex( currentItem() )->query()->id();
|
||||
|
||||
QList<Tomahawk::query_ptr> queries;
|
||||
if ( data->hasFormat( "application/tomahawk.result.list" ) )
|
||||
if ( tracks.count() )
|
||||
{
|
||||
QByteArray itemData = data->data( "application/tomahawk.result.list" );
|
||||
QDataStream stream( &itemData, QIODevice::ReadOnly );
|
||||
|
||||
while ( !stream.atEnd() )
|
||||
{
|
||||
qlonglong qptr;
|
||||
stream >> qptr;
|
||||
|
||||
Tomahawk::result_ptr* result = reinterpret_cast<Tomahawk::result_ptr*>(qptr);
|
||||
if ( result && !result->isNull() )
|
||||
{
|
||||
qDebug() << "Dropped result item:" << result->data()->artist() << "-" << result->data()->track();
|
||||
query_ptr q = result->data()->toQuery();
|
||||
q->addResults( QList< result_ptr >() << *result );
|
||||
queries << q;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( data->hasFormat( "application/tomahawk.query.list" ) )
|
||||
{
|
||||
QByteArray itemData = data->data( "application/tomahawk.query.list" );
|
||||
QDataStream stream( &itemData, QIODevice::ReadOnly );
|
||||
|
||||
while ( !stream.atEnd() )
|
||||
{
|
||||
qlonglong qptr;
|
||||
stream >> qptr;
|
||||
|
||||
Tomahawk::query_ptr* query = reinterpret_cast<Tomahawk::query_ptr*>(qptr);
|
||||
if ( query && !query->isNull() )
|
||||
{
|
||||
qDebug() << "Dropped query item:" << query->data()->artist() << "-" << query->data()->track() << action;
|
||||
queries << *query;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( queries.count() )
|
||||
{
|
||||
emit beginInsertRows( QModelIndex(), beginRow, beginRow + queries.count() - 1 );
|
||||
foreach( const Tomahawk::query_ptr& query, queries )
|
||||
emit beginInsertRows( QModelIndex(), beginRow, beginRow + tracks.count() - 1 );
|
||||
foreach( const Tomahawk::query_ptr& query, tracks )
|
||||
{
|
||||
plentry_ptr e( new PlaylistEntry() );
|
||||
e->setGuid( uuid() );
|
||||
@ -441,11 +420,12 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r
|
||||
}
|
||||
emit endInsertRows();
|
||||
|
||||
if ( action == Qt::CopyAction )
|
||||
if ( m_dropStorage.action == Qt::CopyAction )
|
||||
onPlaylistChanged( true );
|
||||
}
|
||||
|
||||
return true;
|
||||
m_dropStorage.parent = QPersistentModelIndex();
|
||||
m_dropStorage.row = -10;
|
||||
}
|
||||
|
||||
|
||||
|
@ -39,6 +39,12 @@ class DLLEXPORT PlaylistModel : public TrackModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
typedef struct {
|
||||
int row;
|
||||
QPersistentModelIndex parent;
|
||||
Qt::DropAction action;
|
||||
} DropStorageData;
|
||||
|
||||
public:
|
||||
explicit PlaylistModel( QObject* parent = 0 );
|
||||
~PlaylistModel();
|
||||
@ -84,6 +90,7 @@ private slots:
|
||||
|
||||
void onTracksAdded( const QList<Tomahawk::query_ptr>& tracks );
|
||||
void onTracksInserted( unsigned int row, const QList<Tomahawk::query_ptr>& tracks );
|
||||
void parsedDroppedTracks( QList<Tomahawk::query_ptr> );
|
||||
|
||||
void trackResolved( bool );
|
||||
|
||||
@ -93,6 +100,8 @@ private:
|
||||
Tomahawk::playlist_ptr m_playlist;
|
||||
bool m_waitForUpdate, m_isTemporary;
|
||||
QList< Tomahawk::Query* > m_waitingForResolved;
|
||||
|
||||
DropStorageData m_dropStorage;
|
||||
};
|
||||
|
||||
#endif // PLAYLISTMODEL_H
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "dynamic/widgets/LoadingSpinner.h"
|
||||
#include "utils/tomahawkutils.h"
|
||||
#include "utils/logger.h"
|
||||
#include <globalactionmanager.h>
|
||||
|
||||
using namespace Tomahawk;
|
||||
|
||||
@ -214,9 +215,7 @@ TrackView::dragEnterEvent( QDragEnterEvent* event )
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
QTreeView::dragEnterEvent( event );
|
||||
|
||||
if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" )
|
||||
|| event->mimeData()->hasFormat( "application/tomahawk.plentry.list" )
|
||||
|| event->mimeData()->hasFormat( "application/tomahawk.result.list" ) )
|
||||
if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
|
||||
{
|
||||
m_dragging = true;
|
||||
m_dropRect = QRect();
|
||||
@ -238,9 +237,7 @@ TrackView::dragMoveEvent( QDragMoveEvent* event )
|
||||
return;
|
||||
}
|
||||
|
||||
if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" )
|
||||
|| event->mimeData()->hasFormat( "application/tomahawk.plentry.list" )
|
||||
|| event->mimeData()->hasFormat( "application/tomahawk.result.list" ) )
|
||||
if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
|
||||
{
|
||||
setDirtyRegion( m_dropRect );
|
||||
const QPoint pos = event->pos();
|
||||
@ -274,20 +271,7 @@ TrackView::dropEvent( QDropEvent* event )
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) )
|
||||
{
|
||||
const QPoint pos = event->pos();
|
||||
const QModelIndex index = indexAt( pos );
|
||||
|
||||
qDebug() << "Drop Event accepted at row:" << index.row();
|
||||
event->acceptProposedAction();
|
||||
|
||||
if ( !model()->isReadOnly() )
|
||||
{
|
||||
model()->dropMimeData( event->mimeData(), event->proposedAction(), index.row(), 0, index.parent() );
|
||||
}
|
||||
}
|
||||
else if ( event->mimeData()->hasFormat( "application/tomahawk.result.list" ) )
|
||||
if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
|
||||
{
|
||||
const QPoint pos = event->pos();
|
||||
const QModelIndex index = indexAt( pos );
|
||||
|
147
src/libtomahawk/utils/spotifyparser.cpp
Normal file
147
src/libtomahawk/utils/spotifyparser.cpp
Normal file
@ -0,0 +1,147 @@
|
||||
/* === 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 "spotifyparser.h"
|
||||
|
||||
#include "utils/logger.h"
|
||||
#include "utils/tomahawkutils.h"
|
||||
#include "query.h"
|
||||
|
||||
#include <qjson/parser.h>
|
||||
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
using namespace Tomahawk;
|
||||
|
||||
SpotifyParser::SpotifyParser( const QStringList& trackUrls, QObject* parent )
|
||||
: QObject ( parent )
|
||||
, m_single( false )
|
||||
{
|
||||
foreach ( const QString& url, trackUrls )
|
||||
lookupTrack( url );
|
||||
}
|
||||
|
||||
SpotifyParser::SpotifyParser( const QString& trackUrl, QObject* parent )
|
||||
: QObject ( parent )
|
||||
, m_single( true )
|
||||
{
|
||||
lookupTrack( trackUrl );
|
||||
}
|
||||
|
||||
SpotifyParser::~SpotifyParser()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
SpotifyParser::lookupTrack( const QString& link )
|
||||
{
|
||||
if ( !link.contains( "track" ) ) // we only support track links atm
|
||||
return;
|
||||
|
||||
// we need Spotify URIs such as spotify:track:XXXXXX, so if we by chance get a http://open.spotify.com url, convert it
|
||||
QString uri = link;
|
||||
if ( link.contains( "open.spotify.com" ) )
|
||||
{
|
||||
QString hash = link;
|
||||
hash.replace( "http://open.spotify.com/track/", "" );
|
||||
uri = QString( "spotify:track:%1" ).arg( hash );
|
||||
}
|
||||
|
||||
tLog() << "Parsing Spotify Track URI:" << uri;
|
||||
|
||||
QUrl url = QUrl( QString( "http://ws.spotify.com/lookup/1/.json?uri=%1" ).arg( uri ) );
|
||||
tDebug() << "Looking up..." << url.toString();
|
||||
|
||||
QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
|
||||
connect( reply, SIGNAL( finished() ), this, SLOT( spotifyTrackLookupFinished() ) );
|
||||
|
||||
m_queries.insert( reply );
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
SpotifyParser::spotifyTrackLookupFinished()
|
||||
{
|
||||
QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() );
|
||||
Q_ASSERT( r );
|
||||
m_queries.remove( r );
|
||||
r->deleteLater();
|
||||
|
||||
if ( r->error() == QNetworkReply::NoError )
|
||||
{
|
||||
QJson::Parser p;
|
||||
bool ok;
|
||||
QVariantMap res = p.parse( r, &ok ).toMap();
|
||||
|
||||
if ( !ok )
|
||||
{
|
||||
tLog() << "Failed to parse json from Spotify track lookup:" << p.errorString() << "On line" << p.errorLine();
|
||||
checkFinished();
|
||||
return;
|
||||
} else if ( !res.contains( "track" ) )
|
||||
{
|
||||
tLog() << "No 'track' item in the spotify track lookup result... not doing anything";
|
||||
checkFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
// lets parse this baby
|
||||
QVariantMap t = res.value( "track" ).toMap();
|
||||
|
||||
QString title, artist, album;
|
||||
|
||||
title = t.value( "name", QString() ).toString();
|
||||
// TODO for now only take the first artist
|
||||
if ( t.contains( "artists" ) && t[ "artists" ].canConvert< QVariantList >() && t[ "artists" ].toList().size() > 0 )
|
||||
artist = t[ "artists" ].toList().first().toMap().value( "name", QString() ).toString();
|
||||
if ( t.contains( "album" ) && t[ "album" ].canConvert< QVariantMap >() )
|
||||
album = t[ "album" ].toMap().value( "name", QString() ).toString();
|
||||
|
||||
if ( title.isEmpty() && artist.isEmpty() ) // don't have enough...
|
||||
{
|
||||
tLog() << "Didn't get an artist and track name from spotify, not enough to build a query on. Aborting" << title << artist << album;
|
||||
return;
|
||||
}
|
||||
|
||||
Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album, uuid(), true );
|
||||
m_tracks << q;
|
||||
|
||||
} else
|
||||
{
|
||||
tLog() << "Error in network request to Spotify for track decoding:" << r->errorString();
|
||||
}
|
||||
|
||||
checkFinished();
|
||||
}
|
||||
|
||||
void
|
||||
SpotifyParser::checkFinished()
|
||||
{
|
||||
if ( m_queries.isEmpty() ) // we're done
|
||||
{
|
||||
if ( m_single && !m_tracks.isEmpty() )
|
||||
emit track( m_tracks.first() );
|
||||
else if ( !m_single && !m_tracks.isEmpty() )
|
||||
emit tracks( m_tracks );
|
||||
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
}
|
64
src/libtomahawk/utils/spotifyparser.h
Normal file
64
src/libtomahawk/utils/spotifyparser.h
Normal file
@ -0,0 +1,64 @@
|
||||
/* === 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 SPOTIFY_PARSER_H
|
||||
#define SPOTIFY_PARSER_H
|
||||
|
||||
#include "dllmacro.h"
|
||||
#include "typedefs.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
class QNetworkReply;
|
||||
namespace Tomahawk
|
||||
{
|
||||
|
||||
/**
|
||||
* Small class to parse spotify links into query_ptrs
|
||||
*
|
||||
* Connect to the signals to get the results
|
||||
*/
|
||||
class DLLEXPORT SpotifyParser : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SpotifyParser( const QString& trackUrl, QObject* parent = 0 );
|
||||
explicit SpotifyParser( const QStringList& trackUrls, QObject* parent = 0 );
|
||||
virtual ~SpotifyParser();
|
||||
|
||||
signals:
|
||||
void track( const Tomahawk::query_ptr& track );
|
||||
void tracks( const QList< Tomahawk::query_ptr > tracks );
|
||||
|
||||
private slots:
|
||||
void spotifyTrackLookupFinished();
|
||||
|
||||
private:
|
||||
void lookupTrack( const QString& track );
|
||||
void checkFinished();
|
||||
|
||||
bool m_single;
|
||||
QList< query_ptr > m_tracks;
|
||||
QSet< QNetworkReply* > m_queries;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -28,6 +28,7 @@
|
||||
#include "widgets/playlisttypeselectordlg.h"
|
||||
#include <playlist/dynamic/GeneratorInterface.h>
|
||||
#include "utils/logger.h"
|
||||
#include <globalactionmanager.h>
|
||||
|
||||
using namespace Tomahawk;
|
||||
|
||||
@ -121,12 +122,7 @@ CategoryAddItem::icon() const
|
||||
bool
|
||||
CategoryAddItem::willAcceptDrag( const QMimeData* data ) const
|
||||
{
|
||||
if ( ( m_categoryType == SourcesModel::PlaylistsCategory || m_categoryType == SourcesModel::StationsCategory ) &&
|
||||
(
|
||||
data->hasFormat( "application/tomahawk.query.list" ) ||
|
||||
data->hasFormat( "application/tomahawk.result.list" )
|
||||
)
|
||||
)
|
||||
if ( ( m_categoryType == SourcesModel::PlaylistsCategory || m_categoryType == SourcesModel::StationsCategory ) && GlobalActionManager::instance()->acceptsMimeData( data ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -138,78 +134,43 @@ bool
|
||||
CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction )
|
||||
{
|
||||
// Create a new playlist seeded with these items
|
||||
if ( data->hasFormat( "application/tomahawk.query.list" ) || data->hasFormat( "application/tomahawk.result.list" ) ) {
|
||||
connect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
|
||||
GlobalActionManager::instance()->tracksFromMimeData( data );
|
||||
|
||||
QList< Tomahawk::query_ptr > queries;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( data->hasFormat( "application/tomahawk.query.list" ) ) {
|
||||
QByteArray itemData = data->data( "application/tomahawk.query.list" );
|
||||
QDataStream stream( &itemData, QIODevice::ReadOnly );
|
||||
void
|
||||
CategoryAddItem::parsedDroppedTracks( const QList< query_ptr >& tracks )
|
||||
{
|
||||
disconnect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
|
||||
if( m_categoryType == SourcesModel::PlaylistsCategory ) {
|
||||
|
||||
while ( !stream.atEnd() )
|
||||
{
|
||||
qlonglong qptr;
|
||||
stream >> qptr;
|
||||
playlist_ptr newpl = Playlist::create( SourceList::instance()->getLocal(), uuid(), "New Playlist", "", SourceList::instance()->getLocal()->friendlyName(), false, tracks );
|
||||
ViewManager::instance()->show( newpl );
|
||||
|
||||
Tomahawk::query_ptr* query = reinterpret_cast<Tomahawk::query_ptr*>(qptr);
|
||||
if ( query && !query->isNull() )
|
||||
{
|
||||
qDebug() << "Dropped query item:" << query->data()->artist() << "-" << query->data()->track();
|
||||
queries << *query;
|
||||
}
|
||||
}
|
||||
} else if( data->hasFormat( "application/tomahawk.result.list" ) ) {
|
||||
QByteArray itemData = data->data( "application/tomahawk.result.list" );
|
||||
QDataStream stream( &itemData, QIODevice::ReadOnly );
|
||||
// Give a shot to try to rename it. The playlist has to be created first. ugly.
|
||||
QTimer::singleShot( 300, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) );
|
||||
} else if( m_categoryType == SourcesModel::StationsCategory ) {
|
||||
// seed the playlist with these song filters
|
||||
QString name = tracks.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( tracks.first()->track() );
|
||||
dynplaylist_ptr newpl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), name, "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false );
|
||||
newpl->setMode( OnDemand );
|
||||
|
||||
while ( !stream.atEnd() )
|
||||
{
|
||||
qlonglong qptr;
|
||||
stream >> qptr;
|
||||
|
||||
Tomahawk::result_ptr* result = reinterpret_cast<Tomahawk::result_ptr*>(qptr);
|
||||
if ( result && !result->isNull() )
|
||||
{
|
||||
qDebug() << "Dropped result item:" << result->data()->artist() << "-" << result->data()->track();
|
||||
query_ptr q = result->data()->toQuery();
|
||||
q->addResults( QList< result_ptr >() << *result );
|
||||
queries << q;
|
||||
}
|
||||
}
|
||||
// now we want to add each query as a song filter...
|
||||
QList< dyncontrol_ptr > contrls;
|
||||
foreach( const Tomahawk::query_ptr& q, tracks ) {
|
||||
dyncontrol_ptr c = newpl->generator()->createControl( "Song" );
|
||||
c->setInput( QString( "%1 %2" ).arg( q->track() ).arg( q->artist() ) );
|
||||
contrls << c;
|
||||
}
|
||||
|
||||
if( m_categoryType == SourcesModel::PlaylistsCategory ) {
|
||||
newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), contrls );
|
||||
|
||||
playlist_ptr newpl = Playlist::create( SourceList::instance()->getLocal(), uuid(), "New Playlist", "", SourceList::instance()->getLocal()->friendlyName(), false, queries );
|
||||
ViewManager::instance()->show( newpl );
|
||||
|
||||
// Give a shot to try to rename it. The playlist has to be created first. ugly.
|
||||
QTimer::singleShot( 300, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) );
|
||||
} else if( m_categoryType == SourcesModel::StationsCategory ) {
|
||||
// seed the playlist with these song filters
|
||||
QString name = queries.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( queries.first()->track() );
|
||||
dynplaylist_ptr newpl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), name, "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false );
|
||||
newpl->setMode( OnDemand );
|
||||
|
||||
// now we want to add each query as a song filter...
|
||||
QList< dyncontrol_ptr > contrls;
|
||||
foreach( const Tomahawk::query_ptr& q, queries ) {
|
||||
dyncontrol_ptr c = newpl->generator()->createControl( "Song" );
|
||||
c->setInput( QString( "%1 %2" ).arg( q->track() ).arg( q->artist() ) );
|
||||
contrls << c;
|
||||
}
|
||||
|
||||
newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), contrls );
|
||||
|
||||
|
||||
ViewManager::instance()->show( newpl );
|
||||
// Give a shot to try to rename it. The playlist has to be created first. ugly.
|
||||
QTimer::singleShot( 300, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
ViewManager::instance()->show( newpl );
|
||||
// Give a shot to try to rename it. The playlist has to be created first. ugly.
|
||||
QTimer::singleShot( 300, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -35,6 +35,9 @@ public:
|
||||
virtual bool willAcceptDrag(const QMimeData* data) const;
|
||||
virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action);
|
||||
|
||||
private slots:
|
||||
void parsedDroppedTracks( const QList< Tomahawk::query_ptr >& tracks );
|
||||
|
||||
private:
|
||||
SourcesModel::CategoryType m_categoryType;
|
||||
};
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "collectionitem.h"
|
||||
#include "utils/tomahawkutils.h"
|
||||
#include "utils/logger.h"
|
||||
#include "globalactionmanager.h"
|
||||
|
||||
using namespace Tomahawk;
|
||||
|
||||
@ -138,56 +139,23 @@ PlaylistItem::dropMimeData( const QMimeData* data, Qt::DropAction action )
|
||||
data->data( "application/tomahawk.playlist.id" ) == m_playlist->guid() )
|
||||
return false; // don't allow dropping on ourselves
|
||||
|
||||
if ( data->hasFormat( "application/tomahawk.result.list" ) )
|
||||
{
|
||||
QByteArray itemData = data->data( "application/tomahawk.result.list" );
|
||||
QDataStream stream( &itemData, QIODevice::ReadOnly );
|
||||
connect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
|
||||
GlobalActionManager::instance()->tracksFromMimeData( data );
|
||||
|
||||
while ( !stream.atEnd() )
|
||||
{
|
||||
qlonglong qptr;
|
||||
stream >> qptr;
|
||||
// TODO cant' know if it works or not yet...
|
||||
return true;
|
||||
}
|
||||
|
||||
Tomahawk::result_ptr* result = reinterpret_cast<Tomahawk::result_ptr*>(qptr);
|
||||
if ( result && !result->isNull() )
|
||||
{
|
||||
qDebug() << "Dropped result item:" << result->data()->artist() << "-" << result->data()->track();
|
||||
query_ptr q = result->data()->toQuery();
|
||||
q->addResults( QList< result_ptr >() << *result );
|
||||
queries << q;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( data->hasFormat( "application/tomahawk.query.list" ) )
|
||||
{
|
||||
QByteArray itemData = data->data( "application/tomahawk.query.list" );
|
||||
QDataStream stream( &itemData, QIODevice::ReadOnly );
|
||||
|
||||
while ( !stream.atEnd() )
|
||||
{
|
||||
qlonglong qptr;
|
||||
stream >> qptr;
|
||||
|
||||
Tomahawk::query_ptr* query = reinterpret_cast<Tomahawk::query_ptr*>(qptr);
|
||||
if ( query && !query->isNull() )
|
||||
{
|
||||
qDebug() << "Dropped query item:" << query->data()->artist() << "-" << query->data()->track();
|
||||
queries << *query;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( queries.count() && !m_playlist.isNull() && m_playlist->author()->isLocal() )
|
||||
void
|
||||
PlaylistItem::parsedDroppedTracks( const QList< query_ptr >& tracks)
|
||||
{
|
||||
disconnect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
|
||||
if ( tracks.count() && !m_playlist.isNull() && m_playlist->author()->isLocal() )
|
||||
{
|
||||
qDebug() << "on playlist:" << m_playlist->title() << m_playlist->guid() << m_playlist->currentrevision();
|
||||
|
||||
m_playlist->addEntries( queries, m_playlist->currentrevision() );
|
||||
|
||||
return true;
|
||||
m_playlist->addEntries( tracks, m_playlist->currentrevision() );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -43,6 +43,7 @@ protected:
|
||||
private slots:
|
||||
void onPlaylistLoaded( Tomahawk::PlaylistRevision revision );
|
||||
void onPlaylistChanged();
|
||||
void parsedDroppedTracks( const QList<Tomahawk::query_ptr>& tracks );
|
||||
|
||||
private:
|
||||
bool m_loaded;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "viewmanager.h"
|
||||
|
||||
#include "utils/logger.h"
|
||||
#include "globalactionmanager.h"
|
||||
|
||||
using namespace Tomahawk;
|
||||
|
||||
@ -179,10 +180,7 @@ SourcesModel::setData( const QModelIndex& index, const QVariant& value, int role
|
||||
QStringList
|
||||
SourcesModel::mimeTypes() const
|
||||
{
|
||||
QStringList types;
|
||||
types << "application/tomahawk.query.list";
|
||||
types << "application/tomahawk.result.list";
|
||||
return types;
|
||||
return GlobalActionManager::instance()->mimeTypes();
|
||||
}
|
||||
|
||||
|
||||
@ -216,7 +214,11 @@ SourcesModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int ro
|
||||
Qt::DropActions
|
||||
SourcesModel::supportedDropActions() const
|
||||
{
|
||||
#ifdef Q_OS_MAC
|
||||
return Qt::CopyAction | Qt::MoveAction;
|
||||
#else
|
||||
return Qt::CopyAction;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -431,8 +431,7 @@ SourceTreeView::dragEnterEvent( QDragEnterEvent* event )
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
QTreeView::dragEnterEvent( event );
|
||||
|
||||
if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" )
|
||||
|| event->mimeData()->hasFormat( "application/tomahawk.result.list" ) )
|
||||
if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
|
||||
{
|
||||
m_dragging = true;
|
||||
m_dropRect = QRect();
|
||||
@ -460,8 +459,7 @@ SourceTreeView::dragMoveEvent( QDragMoveEvent* event )
|
||||
bool accept = false;
|
||||
QTreeView::dragMoveEvent( event );
|
||||
|
||||
if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" )
|
||||
|| event->mimeData()->hasFormat( "application/tomahawk.result.list" ) )
|
||||
if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
|
||||
{
|
||||
setDirtyRegion( m_dropRect );
|
||||
const QPoint pos = event->pos();
|
||||
|
@ -516,7 +516,7 @@ TomahawkApp::loadUrl( const QString& url )
|
||||
if ( url.startsWith( "tomahawk://" ) )
|
||||
return GlobalActionManager::instance()->parseTomahawkLink( url );
|
||||
else if ( url.contains( "open.spotify.com" ) || url.contains( "spotify:track" ) )
|
||||
return GlobalActionManager::instance()->parseSpotifyLink( url );
|
||||
return GlobalActionManager::instance()->openSpotifyLink( url );
|
||||
else
|
||||
{
|
||||
QFile f( url );
|
||||
|
Loading…
x
Reference in New Issue
Block a user