1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-08-11 08:34:34 +02:00

Merge branch 'dragndrop'

This commit is contained in:
Leo Franchi
2011-08-05 07:57:26 -04:00
17 changed files with 498 additions and 325 deletions

View File

@@ -167,6 +167,7 @@ set( libSources
utils/xspfloader.cpp utils/xspfloader.cpp
utils/xspfgenerator.cpp utils/xspfgenerator.cpp
utils/jspfloader.cpp utils/jspfloader.cpp
utils/spotifyparser.cpp
widgets/newplaylistwidget.cpp widgets/newplaylistwidget.cpp
widgets/searchwidget.cpp widgets/searchwidget.cpp
@@ -338,6 +339,7 @@ set( libHeaders
utils/xspfloader.h utils/xspfloader.h
utils/xspfgenerator.h utils/xspfgenerator.h
utils/jspfloader.h utils/jspfloader.h
utils/spotifyparser.h
widgets/newplaylistwidget.h widgets/newplaylistwidget.h
widgets/searchwidget.h widgets/searchwidget.h

View File

@@ -22,6 +22,7 @@
#include <QClipboard> #include <QClipboard>
#include <QUrl> #include <QUrl>
#include "artist.h"
#include "album.h" #include "album.h"
#include "sourcelist.h" #include "sourcelist.h"
#include "pipeline.h" #include "pipeline.h"
@@ -42,9 +43,13 @@
#include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include "utils/jspfloader.h" #include "utils/jspfloader.h"
#include <QMimeData>
#include "utils/spotifyparser.h"
GlobalActionManager* GlobalActionManager::s_instance = 0; GlobalActionManager* GlobalActionManager::s_instance = 0;
using namespace Tomahawk;
GlobalActionManager* GlobalActionManager*
GlobalActionManager::instance() GlobalActionManager::instance()
{ {
@@ -54,18 +59,17 @@ GlobalActionManager::instance()
return s_instance; return s_instance;
} }
GlobalActionManager::GlobalActionManager( QObject* parent ) GlobalActionManager::GlobalActionManager( QObject* parent )
: QObject( parent ) : QObject( parent )
{ {
m_mimeTypes << "application/tomahawk.query.list" << "application/tomahawk.plentry.list" << "application/tomahawk.result.list" << "text/plain";
} }
GlobalActionManager::~GlobalActionManager() GlobalActionManager::~GlobalActionManager()
{} {}
QUrl QUrl
GlobalActionManager::openLinkFromQuery( const Tomahawk::query_ptr& query ) const GlobalActionManager::openLinkFromQuery( const query_ptr& query ) const
{ {
QString title, artist, album; QString title, artist, album;
@@ -100,9 +104,9 @@ GlobalActionManager::openLink( const QString& title, const QString& artist, cons
} }
QString 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" ) { if( playlist->generator()->type() != "echonest" ) {
tLog() << "Only echonest generators are supported"; tLog() << "Only echonest generators are supported";
@@ -112,8 +116,8 @@ GlobalActionManager::copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& p
link.addEncodedQueryItem( "type", "echonest" ); link.addEncodedQueryItem( "type", "echonest" );
link.addQueryItem( "title", playlist->title() ); link.addQueryItem( "title", playlist->title() );
QList< Tomahawk::dyncontrol_ptr > controls = playlist->generator()->controls(); QList< dyncontrol_ptr > controls = playlist->generator()->controls();
foreach( const Tomahawk::dyncontrol_ptr& c, controls ) { foreach( const dyncontrol_ptr& c, controls ) {
if( c->selectedType() == "Artist" ) { if( c->selectedType() == "Artist" ) {
if( c->match().toInt() == Echonest::DynamicPlaylist::ArtistType ) if( c->match().toInt() == Echonest::DynamicPlaylist::ArtistType )
link.addQueryItem( "artist_limitto", c->input() ); link.addQueryItem( "artist_limitto", c->input() );
@@ -142,7 +146,7 @@ GlobalActionManager::copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& p
} }
void 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 ); XSPFGenerator* g = new XSPFGenerator( playlist, this );
g->setProperty( "filename", filename ); g->setProperty( "filename", filename );
@@ -169,7 +173,7 @@ GlobalActionManager::xspfCreated( const QByteArray& xspf )
void void
GlobalActionManager::copyToClipboard( const Tomahawk::query_ptr& query ) const GlobalActionManager::copyToClipboard( const query_ptr& query ) const
{ {
QClipboard* cb = QApplication::clipboard(); QClipboard* cb = QApplication::clipboard();
cb->setText( openLinkFromQuery( query ).toEncoded() ); cb->setText( openLinkFromQuery( query ).toEncoded() );
@@ -199,7 +203,7 @@ GlobalActionManager::parseTomahawkLink( const QString& url )
return true; return true;
} else if( u.hasQueryItem( "jspf" ) ) { } else if( u.hasQueryItem( "jspf" ) ) {
QUrl jspf = QUrl::fromUserInput( u.queryItemValue( "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(); tDebug() << "Loading jspiff:" << jspf.toString();
l->load( jspf ); l->load( jspf );
connect( l, SIGNAL( ok( Tomahawk::playlist_ptr ) ), ViewManager::instance(), SLOT( show( Tomahawk::playlist_ptr ) ) ); 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..."; tLog() << "New playlist command needs a title...";
return false; 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 ); ViewManager::instance()->show( pl );
} else if( parts[ 0 ] == "add" ) { } else if( parts[ 0 ] == "add" ) {
if( !url.hasQueryItem( "playlistid" ) || !url.hasQueryItem( "title" ) || !url.hasQueryItem( "artist" ) ) { if( !url.hasQueryItem( "playlistid" ) || !url.hasQueryItem( "title" ) || !url.hasQueryItem( "artist" ) ) {
@@ -305,7 +309,7 @@ GlobalActionManager::handleOpenCommand(const QUrl& url)
} }
void void
GlobalActionManager::handleOpenTrack ( const Tomahawk::query_ptr& q ) GlobalActionManager::handleOpenTrack ( const query_ptr& q )
{ {
ViewManager::instance()->queue()->model()->append( q ); ViewManager::instance()->queue()->model()->append( q );
ViewManager::instance()->showQueue(); 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 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() ) if( !urlStr.isEmpty() )
q->setResultHint( urlStr ); q->setResultHint( urlStr );
Tomahawk::Pipeline::instance()->resolve( q, true ); Pipeline::instance()->resolve( q, true );
handleOpenTrack( q ); handleOpenTrack( q );
return true; return true;
@@ -373,10 +377,10 @@ GlobalActionManager::doQueueAdd( const QStringList& parts, const QList< QPair< Q
// TODO // TODO
} else { // give it a web result hint } else { // give it a web result hint
QFileInfo info( track.path() ); 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() ); q->setResultHint( track.toString() );
Tomahawk::Pipeline::instance()->resolve( q, true ); Pipeline::instance()->resolve( q, true );
ViewManager::instance()->queue()->model()->append( q ); ViewManager::instance()->queue()->model()->append( q );
ViewManager::instance()->showQueue(); ViewManager::instance()->showQueue();
@@ -415,139 +419,139 @@ GlobalActionManager::handleAutoPlaylistCommand( const QUrl& url )
return !loadDynamicPlaylist( url, false ).isNull(); return !loadDynamicPlaylist( url, false ).isNull();
} }
Tomahawk::dynplaylist_ptr dynplaylist_ptr
GlobalActionManager::loadDynamicPlaylist( const QUrl& url, bool station ) GlobalActionManager::loadDynamicPlaylist( const QUrl& url, bool station )
{ {
QStringList parts = url.path().split( "/" ).mid( 1 ); // get the rest of the command QStringList parts = url.path().split( "/" ).mid( 1 ); // get the rest of the command
if( parts.isEmpty() ) { if( parts.isEmpty() ) {
tLog() << "No specific station command:" << url.toString(); tLog() << "No specific station command:" << url.toString();
return Tomahawk::dynplaylist_ptr(); return dynplaylist_ptr();
} }
if( parts[ 0 ] == "create" ) { if( parts[ 0 ] == "create" ) {
if( !url.hasQueryItem( "title" ) || !url.hasQueryItem( "type" ) ) { if( !url.hasQueryItem( "title" ) || !url.hasQueryItem( "type" ) ) {
tLog() << "Station create command needs title and type..." << url.toString(); tLog() << "Station create command needs title and type..." << url.toString();
return Tomahawk::dynplaylist_ptr(); return dynplaylist_ptr();
} }
QString title = url.queryItemValue( "title" ); QString title = url.queryItemValue( "title" );
QString type = url.queryItemValue( "type" ); QString type = url.queryItemValue( "type" );
Tomahawk::GeneratorMode m = Tomahawk::Static; GeneratorMode m = Static;
if( station ) 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 ); pl->setMode( m );
QList< Tomahawk::dyncontrol_ptr > controls; QList< dyncontrol_ptr > controls;
QPair< QString, QString > param; QPair< QString, QString > param;
foreach( param, url.queryItems() ) { foreach( param, url.queryItems() ) {
if( param.first == "artist" ) { if( param.first == "artist" ) {
Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Artist" ); dyncontrol_ptr c = pl->generator()->createControl( "Artist" );
c->setInput( param.second ); c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistRadioType ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistRadioType ) );
controls << c; controls << c;
} else if( param.first == "artist_limitto" ) { } 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->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistType ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistType ) );
controls << c; controls << c;
} else if( param.first == "description" ) { } 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->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistDescriptionType ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistDescriptionType ) );
controls << c; controls << c;
} else if( param.first == "variety" ) { } 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->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Variety ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Variety ) );
controls << c; controls << c;
} else if( param.first.startsWith( "tempo" ) ) { } 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; int extra = param.first.endsWith( "_max" ) ? -1 : 0;
c->setInput( param.second ); c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinTempo + extra ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinTempo + extra ) );
controls << c; controls << c;
} else if( param.first.startsWith( "duration" ) ) { } 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; int extra = param.first.endsWith( "_max" ) ? -1 : 0;
c->setInput( param.second ); c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinDuration + extra ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinDuration + extra ) );
controls << c; controls << c;
} else if( param.first.startsWith( "loudness" ) ) { } 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; int extra = param.first.endsWith( "_max" ) ? -1 : 0;
c->setInput( param.second ); c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinLoudness + extra ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinLoudness + extra ) );
controls << c; controls << c;
} else if( param.first.startsWith( "danceability" ) ) { } 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; int extra = param.first.endsWith( "_max" ) ? 1 : 0;
c->setInput( param.second ); c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinDanceability + extra ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinDanceability + extra ) );
controls << c; controls << c;
} else if( param.first.startsWith( "energy" ) ) { } 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; int extra = param.first.endsWith( "_max" ) ? 1 : 0;
c->setInput( param.second ); c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinEnergy + extra ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinEnergy + extra ) );
controls << c; controls << c;
} else if( param.first.startsWith( "artist_familiarity" ) ) { } 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; int extra = param.first.endsWith( "_max" ) ? -1 : 0;
c->setInput( param.second ); c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinFamiliarity + extra ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinFamiliarity + extra ) );
controls << c; controls << c;
} else if( param.first.startsWith( "artist_hotttnesss" ) ) { } 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; int extra = param.first.endsWith( "_max" ) ? -1 : 0;
c->setInput( param.second ); c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinHotttnesss + extra ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinHotttnesss + extra ) );
controls << c; controls << c;
} else if( param.first.startsWith( "song_hotttnesss" ) ) { } 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; int extra = param.first.endsWith( "_max" ) ? -1 : 0;
c->setInput( param.second ); c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::SongMinHotttnesss + extra ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::SongMinHotttnesss + extra ) );
controls << c; controls << c;
} else if( param.first.startsWith( "longitude" ) ) { } 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; int extra = param.first.endsWith( "_max" ) ? 1 : 0;
c->setInput( param.second ); c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinLongitude + extra ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinLongitude + extra ) );
controls << c; controls << c;
} else if( param.first.startsWith( "latitude" ) ) { } 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; int extra = param.first.endsWith( "_max" ) ? 1 : 0;
c->setInput( param.second ); c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinLatitude + extra ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinLatitude + extra ) );
controls << c; controls << c;
} else if( param.first == "key" ) { } 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->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Key ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Key ) );
controls << c; controls << c;
} else if( param.first == "mode" ) { } 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->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Mode ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Mode ) );
controls << c; controls << c;
} else if( param.first == "mood" ) { } 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->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Mood ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Mood ) );
controls << c; controls << c;
} else if( param.first == "style" ) { } 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->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Style ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Style ) );
controls << c; controls << c;
} else if( param.first == "song" ) { } 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->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::SongRadioType ) ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::SongRadioType ) );
controls << c; controls << c;
} }
} }
if( m == Tomahawk::OnDemand ) if( m == OnDemand )
pl->createNewRevision( uuid(), pl->currentrevision(), type, controls ); pl->createNewRevision( uuid(), pl->currentrevision(), type, controls );
else else
pl->createNewRevision( uuid(), pl->currentrevision(), type, controls, pl->entries() ); pl->createNewRevision( uuid(), pl->currentrevision(), type, controls, pl->entries() );
@@ -555,7 +559,7 @@ GlobalActionManager::loadDynamicPlaylist( const QUrl& url, bool station )
return pl; return pl;
} }
return Tomahawk::dynplaylist_ptr(); return dynplaylist_ptr();
} }
@@ -587,10 +591,10 @@ GlobalActionManager::handlePlayCommand( const QUrl& url )
else if( pair.first == "url" ) else if( pair.first == "url" )
urlStr = pair.second; urlStr = pair.second;
} }
Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album ); query_ptr q = Query::get( artist, title, album );
if( !urlStr.isEmpty() ) if( !urlStr.isEmpty() )
q->setResultHint( urlStr ); q->setResultHint( urlStr );
Tomahawk::Pipeline::instance()->resolve( q, true ); Pipeline::instance()->resolve( q, true );
m_waitingToPlay = q; m_waitingToPlay = q;
connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( waitingForResolved( bool ) ) ); 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" ) else if( pair.first == "url" )
urlStr = pair.second; urlStr = pair.second;
} }
Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album ); query_ptr q = Query::get( artist, title, album );
if( !urlStr.isEmpty() ) if( !urlStr.isEmpty() )
q->setResultHint( urlStr ); 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 // 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 >(); 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 if( bookmarkpl.isNull() ) { // create it and do the deed then
m_waitingToBookmark = q; m_waitingToBookmark = q;
col->createBookmarksPlaylist(); col->createBookmarksPlaylist();
@@ -647,16 +651,16 @@ bool GlobalActionManager::handleBookmarkCommand(const QUrl& url)
void void
GlobalActionManager::bookmarkPlaylistCreated( const Tomahawk::playlist_ptr& pl ) GlobalActionManager::bookmarkPlaylistCreated( const playlist_ptr& pl )
{ {
Q_ASSERT( !m_waitingToBookmark.isNull() ); Q_ASSERT( !m_waitingToBookmark.isNull() );
doBookmark( pl, m_waitingToBookmark ); doBookmark( pl, m_waitingToBookmark );
} }
void 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() ); e->setGuid( uuid() );
if ( q->results().count() ) if ( q->results().count() )
@@ -668,7 +672,7 @@ GlobalActionManager::doBookmark( const Tomahawk::playlist_ptr& pl, const Tomahaw
e->setAnnotation( "" ); // FIXME e->setAnnotation( "" ); // FIXME
e->setQuery( q ); 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() ) ); connect( pl.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( showPlaylist() ) );
m_toShow = pl; m_toShow = pl;
@@ -712,82 +716,111 @@ GlobalActionManager::hostname() const
return QString( "http://toma.hk" ); return QString( "http://toma.hk" );
} }
/// SPOTIFY URL HANDLING /// QMIMEDATA HANDLING
bool QStringList
GlobalActionManager::parseSpotifyLink( const QString& link ) GlobalActionManager::mimeTypes() const
{ {
if( !link.contains( "track" ) ) // we only support track links atm return m_mimeTypes;
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
} }
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 void
GlobalActionManager::spotifyTrackLookupFinished() GlobalActionManager::tracksFromMimeData( const QMimeData* data )
{ {
QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() ); if ( data->hasFormat( "application/tomahawk.query.list" ) )
Q_ASSERT( r ); emit tracks( tracksFromQueryList( data ) );
else if ( data->hasFormat( "application/tomahawk.result.list" ) )
emit tracks( tracksFromResultList( data ) );
else if ( data->hasFormat( "text/plain" ) )
{
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( r->error() == QNetworkReply::NoError ) tDebug() << "Got a list of spotify urls!" << tracks;
{ SpotifyParser* spot = new SpotifyParser( tracks, this );
QJson::Parser p; connect( spot, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ) );
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();
return;
} else if( !res.contains( "track" ) )
{
tLog() << "No 'track' item in the spotify track lookup result... not doing anything";
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 );
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;
}

View File

@@ -28,6 +28,9 @@
#include <QObject> #include <QObject>
#include <QUrl> #include <QUrl>
/**
* Handles global actions such as parsing and creation of links, mime data handling, etc
*/
class DLLEXPORT GlobalActionManager : public QObject class DLLEXPORT GlobalActionManager : public QObject
{ {
Q_OBJECT Q_OBJECT
@@ -38,32 +41,47 @@ public:
QUrl openLinkFromQuery( const Tomahawk::query_ptr& query ) const; QUrl openLinkFromQuery( const Tomahawk::query_ptr& query ) const;
QUrl openLink( const QString& title, const QString& artist, const QString& album ) const; QUrl openLink( const QString& title, const QString& artist, const QString& album ) const;
// spotify /// Takes a spotify link and performs the default open action on it
bool parseSpotifyLink( const QString& link ); bool openSpotifyLink( const QString& link );
void copyToClipboard( const Tomahawk::query_ptr& query ) const; void copyToClipboard( const Tomahawk::query_ptr& query ) const;
QString copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& playlist ); QString copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& playlist );
void savePlaylistToFile( const Tomahawk::playlist_ptr& playlist, const QString& filename ); 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: public slots:
bool parseTomahawkLink( const QString& link ); bool parseTomahawkLink( const QString& link );
void waitingForResolved( bool ); void waitingForResolved( bool );
Tomahawk::dynplaylist_ptr loadDynamicPlaylist( const QUrl& url, bool station ); Tomahawk::dynplaylist_ptr loadDynamicPlaylist( const QUrl& url, bool station );
signals:
/// QMimeData parsing results
void tracks( const QList< Tomahawk::query_ptr >& tracks );
private slots: private slots:
void bookmarkPlaylistCreated( const Tomahawk::playlist_ptr& pl ); void bookmarkPlaylistCreated( const Tomahawk::playlist_ptr& pl );
void showPlaylist(); void showPlaylist();
void xspfCreated( const QByteArray& xspf ); void xspfCreated( const QByteArray& xspf );
// SPOTIFY void handleOpenTrack( const Tomahawk::query_ptr& qry );
void spotifyTrackLookupFinished();
private: private:
explicit GlobalActionManager( QObject* parent = 0 ); explicit GlobalActionManager( QObject* parent = 0 );
void doBookmark( const Tomahawk::playlist_ptr& pl, const Tomahawk::query_ptr& q ); void doBookmark( const Tomahawk::playlist_ptr& pl, const Tomahawk::query_ptr& q );
/// handle opening of urls
bool handlePlaylistCommand( const QUrl& url ); bool handlePlaylistCommand( const QUrl& url );
bool handleCollectionCommand(const QUrl& url ); bool handleCollectionCommand(const QUrl& url );
bool handleQueueCommand(const QUrl& url ); bool handleQueueCommand(const QUrl& url );
@@ -73,16 +91,19 @@ private:
bool handlePlayCommand(const QUrl& url ); bool handlePlayCommand(const QUrl& url );
bool handleBookmarkCommand(const QUrl& url ); bool handleBookmarkCommand(const QUrl& url );
bool handleOpenCommand(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 ); 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; QString hostname() const;
Tomahawk::playlist_ptr m_toShow; Tomahawk::playlist_ptr m_toShow;
Tomahawk::query_ptr m_waitingToBookmark; Tomahawk::query_ptr m_waitingToBookmark;
Tomahawk::query_ptr m_waitingToPlay; Tomahawk::query_ptr m_waitingToPlay;
QStringList m_mimeTypes;
static GlobalActionManager* s_instance; static GlobalActionManager* s_instance;
}; };

View File

@@ -1,6 +1,6 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === /* === 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 * Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === /* === 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 * Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@@ -27,6 +27,7 @@
#include "database/databasecommand_playbackhistory.h" #include "database/databasecommand_playbackhistory.h"
#include "dynamic/GeneratorInterface.h" #include "dynamic/GeneratorInterface.h"
#include "utils/logger.h" #include "utils/logger.h"
#include "globalactionmanager.h"
using namespace Tomahawk; using namespace Tomahawk;
@@ -38,6 +39,9 @@ PlaylistModel::PlaylistModel( QObject* parent )
{ {
qDebug() << Q_FUNC_INFO; qDebug() << Q_FUNC_INFO;
m_dropStorage.parent = QPersistentModelIndex();
m_dropStorage.row = -10;
setReadOnly( false ); setReadOnly( false );
} }
@@ -168,7 +172,7 @@ PlaylistModel::clear()
{ {
if ( rowCount( QModelIndex() ) ) if ( rowCount( QModelIndex() ) )
{ {
emit loadingFinished();; emit loadingFinished();
emit beginResetModel(); emit beginResetModel();
delete m_rootItem; delete m_rootItem;
@@ -355,16 +359,32 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r
if ( action == Qt::IgnoreAction || isReadOnly() ) if ( action == Qt::IgnoreAction || isReadOnly() )
return true; return true;
if ( !data->hasFormat( "application/tomahawk.query.list" ) if ( !GlobalActionManager::instance()->acceptsMimeData( data ) )
&& !data->hasFormat( "application/tomahawk.plentry.list" )
&& !data->hasFormat( "application/tomahawk.result.list" ) )
return false; 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; int beginRow;
if ( row != -1 ) if ( m_dropStorage.row != -1 )
beginRow = row; beginRow = m_dropStorage.row;
else if ( parent.isValid() ) else if ( m_dropStorage.parent.isValid() )
beginRow = parent.row(); beginRow = m_dropStorage.parent.row();
else else
beginRow = rowCount( QModelIndex() ); beginRow = rowCount( QModelIndex() );
@@ -373,51 +393,10 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r
if ( currentItem().isValid() ) if ( currentItem().isValid() )
currentuuid = itemFromIndex( currentItem() )->query()->id(); currentuuid = itemFromIndex( currentItem() )->query()->id();
QList<Tomahawk::query_ptr> queries; if ( tracks.count() )
if ( data->hasFormat( "application/tomahawk.result.list" ) )
{ {
QByteArray itemData = data->data( "application/tomahawk.result.list" ); emit beginInsertRows( QModelIndex(), beginRow, beginRow + tracks.count() - 1 );
QDataStream stream( &itemData, QIODevice::ReadOnly ); foreach( const Tomahawk::query_ptr& query, tracks )
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 )
{ {
plentry_ptr e( new PlaylistEntry() ); plentry_ptr e( new PlaylistEntry() );
e->setGuid( uuid() ); e->setGuid( uuid() );
@@ -441,11 +420,12 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r
} }
emit endInsertRows(); emit endInsertRows();
if ( action == Qt::CopyAction ) if ( m_dropStorage.action == Qt::CopyAction )
onPlaylistChanged( true ); onPlaylistChanged( true );
} }
return true; m_dropStorage.parent = QPersistentModelIndex();
m_dropStorage.row = -10;
} }

View File

@@ -39,6 +39,12 @@ class DLLEXPORT PlaylistModel : public TrackModel
{ {
Q_OBJECT Q_OBJECT
typedef struct {
int row;
QPersistentModelIndex parent;
Qt::DropAction action;
} DropStorageData;
public: public:
explicit PlaylistModel( QObject* parent = 0 ); explicit PlaylistModel( QObject* parent = 0 );
~PlaylistModel(); ~PlaylistModel();
@@ -84,6 +90,7 @@ private slots:
void onTracksAdded( const QList<Tomahawk::query_ptr>& tracks ); void onTracksAdded( const QList<Tomahawk::query_ptr>& tracks );
void onTracksInserted( unsigned int row, 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 ); void trackResolved( bool );
@@ -93,6 +100,8 @@ private:
Tomahawk::playlist_ptr m_playlist; Tomahawk::playlist_ptr m_playlist;
bool m_waitForUpdate, m_isTemporary; bool m_waitForUpdate, m_isTemporary;
QList< Tomahawk::Query* > m_waitingForResolved; QList< Tomahawk::Query* > m_waitingForResolved;
DropStorageData m_dropStorage;
}; };
#endif // PLAYLISTMODEL_H #endif // PLAYLISTMODEL_H

View File

@@ -34,6 +34,7 @@
#include "dynamic/widgets/LoadingSpinner.h" #include "dynamic/widgets/LoadingSpinner.h"
#include "utils/tomahawkutils.h" #include "utils/tomahawkutils.h"
#include "utils/logger.h" #include "utils/logger.h"
#include <globalactionmanager.h>
using namespace Tomahawk; using namespace Tomahawk;
@@ -214,9 +215,7 @@ TrackView::dragEnterEvent( QDragEnterEvent* event )
qDebug() << Q_FUNC_INFO; qDebug() << Q_FUNC_INFO;
QTreeView::dragEnterEvent( event ); QTreeView::dragEnterEvent( event );
if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
|| event->mimeData()->hasFormat( "application/tomahawk.plentry.list" )
|| event->mimeData()->hasFormat( "application/tomahawk.result.list" ) )
{ {
m_dragging = true; m_dragging = true;
m_dropRect = QRect(); m_dropRect = QRect();
@@ -238,9 +237,7 @@ TrackView::dragMoveEvent( QDragMoveEvent* event )
return; return;
} }
if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
|| event->mimeData()->hasFormat( "application/tomahawk.plentry.list" )
|| event->mimeData()->hasFormat( "application/tomahawk.result.list" ) )
{ {
setDirtyRegion( m_dropRect ); setDirtyRegion( m_dropRect );
const QPoint pos = event->pos(); const QPoint pos = event->pos();
@@ -274,20 +271,7 @@ TrackView::dropEvent( QDropEvent* event )
} }
else else
{ {
if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) ) if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
{
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" ) )
{ {
const QPoint pos = event->pos(); const QPoint pos = event->pos();
const QModelIndex index = indexAt( pos ); const QModelIndex index = indexAt( pos );

View 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();
}
}

View 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

View File

@@ -28,6 +28,7 @@
#include "widgets/playlisttypeselectordlg.h" #include "widgets/playlisttypeselectordlg.h"
#include <playlist/dynamic/GeneratorInterface.h> #include <playlist/dynamic/GeneratorInterface.h>
#include "utils/logger.h" #include "utils/logger.h"
#include <globalactionmanager.h>
using namespace Tomahawk; using namespace Tomahawk;
@@ -121,12 +122,7 @@ CategoryAddItem::icon() const
bool bool
CategoryAddItem::willAcceptDrag( const QMimeData* data ) const CategoryAddItem::willAcceptDrag( const QMimeData* data ) const
{ {
if ( ( m_categoryType == SourcesModel::PlaylistsCategory || m_categoryType == SourcesModel::StationsCategory ) && if ( ( m_categoryType == SourcesModel::PlaylistsCategory || m_categoryType == SourcesModel::StationsCategory ) && GlobalActionManager::instance()->acceptsMimeData( data ) )
(
data->hasFormat( "application/tomahawk.query.list" ) ||
data->hasFormat( "application/tomahawk.result.list" )
)
)
{ {
return true; return true;
} }
@@ -138,62 +134,32 @@ bool
CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction ) CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction )
{ {
// Create a new playlist seeded with these items // 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 );
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;
}
}
} else if( data->hasFormat( "application/tomahawk.result.list" ) ) {
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;
}
}
}
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 ) { if( m_categoryType == SourcesModel::PlaylistsCategory ) {
playlist_ptr newpl = Playlist::create( SourceList::instance()->getLocal(), uuid(), "New Playlist", "", SourceList::instance()->getLocal()->friendlyName(), false, queries ); playlist_ptr newpl = Playlist::create( SourceList::instance()->getLocal(), uuid(), "New Playlist", "", SourceList::instance()->getLocal()->friendlyName(), false, tracks );
ViewManager::instance()->show( newpl ); ViewManager::instance()->show( newpl );
// Give a shot to try to rename it. The playlist has to be created first. ugly. // Give a shot to try to rename it. The playlist has to be created first. ugly.
QTimer::singleShot( 300, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) ); QTimer::singleShot( 300, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) );
} else if( m_categoryType == SourcesModel::StationsCategory ) { } else if( m_categoryType == SourcesModel::StationsCategory ) {
// seed the playlist with these song filters // seed the playlist with these song filters
QString name = queries.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( queries.first()->track() ); 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 ); dynplaylist_ptr newpl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), name, "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false );
newpl->setMode( OnDemand ); newpl->setMode( OnDemand );
// now we want to add each query as a song filter... // now we want to add each query as a song filter...
QList< dyncontrol_ptr > contrls; QList< dyncontrol_ptr > contrls;
foreach( const Tomahawk::query_ptr& q, queries ) { foreach( const Tomahawk::query_ptr& q, tracks ) {
dyncontrol_ptr c = newpl->generator()->createControl( "Song" ); dyncontrol_ptr c = newpl->generator()->createControl( "Song" );
c->setInput( QString( "%1 %2" ).arg( q->track() ).arg( q->artist() ) ); c->setInput( QString( "%1 %2" ).arg( q->track() ).arg( q->artist() ) );
contrls << c; contrls << c;
@@ -201,15 +167,10 @@ CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction )
newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), contrls ); newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), contrls );
ViewManager::instance()->show( newpl ); ViewManager::instance()->show( newpl );
// Give a shot to try to rename it. The playlist has to be created first. ugly. // Give a shot to try to rename it. The playlist has to be created first. ugly.
QTimer::singleShot( 300, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) ); QTimer::singleShot( 300, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) );
} }
return true;
}
return false;
} }

View File

@@ -35,6 +35,9 @@ public:
virtual bool willAcceptDrag(const QMimeData* data) const; virtual bool willAcceptDrag(const QMimeData* data) const;
virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action); virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action);
private slots:
void parsedDroppedTracks( const QList< Tomahawk::query_ptr >& tracks );
private: private:
SourcesModel::CategoryType m_categoryType; SourcesModel::CategoryType m_categoryType;
}; };

View File

@@ -25,6 +25,7 @@
#include "collectionitem.h" #include "collectionitem.h"
#include "utils/tomahawkutils.h" #include "utils/tomahawkutils.h"
#include "utils/logger.h" #include "utils/logger.h"
#include "globalactionmanager.h"
using namespace Tomahawk; using namespace Tomahawk;
@@ -138,56 +139,23 @@ PlaylistItem::dropMimeData( const QMimeData* data, Qt::DropAction action )
data->data( "application/tomahawk.playlist.id" ) == m_playlist->guid() ) data->data( "application/tomahawk.playlist.id" ) == m_playlist->guid() )
return false; // don't allow dropping on ourselves return false; // don't allow dropping on ourselves
if ( 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 );
QByteArray itemData = data->data( "application/tomahawk.result.list" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() ) // TODO cant' know if it works or not yet...
{ return true;
qlonglong qptr; }
stream >> qptr;
Tomahawk::result_ptr* result = reinterpret_cast<Tomahawk::result_ptr*>(qptr); void
if ( result && !result->isNull() ) PlaylistItem::parsedDroppedTracks( const QList< query_ptr >& tracks)
{ {
qDebug() << "Dropped result item:" << result->data()->artist() << "-" << result->data()->track(); disconnect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
query_ptr q = result->data()->toQuery(); if ( tracks.count() && !m_playlist.isNull() && m_playlist->author()->isLocal() )
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() )
{ {
qDebug() << "on playlist:" << m_playlist->title() << m_playlist->guid() << m_playlist->currentrevision(); qDebug() << "on playlist:" << m_playlist->title() << m_playlist->guid() << m_playlist->currentrevision();
m_playlist->addEntries( queries, m_playlist->currentrevision() ); m_playlist->addEntries( tracks, m_playlist->currentrevision() );
return true;
} }
return false;
} }

View File

@@ -43,6 +43,7 @@ protected:
private slots: private slots:
void onPlaylistLoaded( Tomahawk::PlaylistRevision revision ); void onPlaylistLoaded( Tomahawk::PlaylistRevision revision );
void onPlaylistChanged(); void onPlaylistChanged();
void parsedDroppedTracks( const QList<Tomahawk::query_ptr>& tracks );
private: private:
bool m_loaded; bool m_loaded;

View File

@@ -32,6 +32,7 @@
#include "viewmanager.h" #include "viewmanager.h"
#include "utils/logger.h" #include "utils/logger.h"
#include "globalactionmanager.h"
using namespace Tomahawk; using namespace Tomahawk;
@@ -179,10 +180,7 @@ SourcesModel::setData( const QModelIndex& index, const QVariant& value, int role
QStringList QStringList
SourcesModel::mimeTypes() const SourcesModel::mimeTypes() const
{ {
QStringList types; return GlobalActionManager::instance()->mimeTypes();
types << "application/tomahawk.query.list";
types << "application/tomahawk.result.list";
return types;
} }
@@ -216,7 +214,11 @@ SourcesModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int ro
Qt::DropActions Qt::DropActions
SourcesModel::supportedDropActions() const SourcesModel::supportedDropActions() const
{ {
#ifdef Q_OS_MAC
return Qt::CopyAction | Qt::MoveAction;
#else
return Qt::CopyAction; return Qt::CopyAction;
#endif
} }

View File

@@ -431,8 +431,7 @@ SourceTreeView::dragEnterEvent( QDragEnterEvent* event )
qDebug() << Q_FUNC_INFO; qDebug() << Q_FUNC_INFO;
QTreeView::dragEnterEvent( event ); QTreeView::dragEnterEvent( event );
if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
|| event->mimeData()->hasFormat( "application/tomahawk.result.list" ) )
{ {
m_dragging = true; m_dragging = true;
m_dropRect = QRect(); m_dropRect = QRect();
@@ -460,8 +459,7 @@ SourceTreeView::dragMoveEvent( QDragMoveEvent* event )
bool accept = false; bool accept = false;
QTreeView::dragMoveEvent( event ); QTreeView::dragMoveEvent( event );
if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
|| event->mimeData()->hasFormat( "application/tomahawk.result.list" ) )
{ {
setDirtyRegion( m_dropRect ); setDirtyRegion( m_dropRect );
const QPoint pos = event->pos(); const QPoint pos = event->pos();

View File

@@ -516,7 +516,7 @@ TomahawkApp::loadUrl( const QString& url )
if ( url.startsWith( "tomahawk://" ) ) if ( url.startsWith( "tomahawk://" ) )
return GlobalActionManager::instance()->parseTomahawkLink( url ); return GlobalActionManager::instance()->parseTomahawkLink( url );
else if ( url.contains( "open.spotify.com" ) || url.contains( "spotify:track" ) ) else if ( url.contains( "open.spotify.com" ) || url.contains( "spotify:track" ) )
return GlobalActionManager::instance()->parseSpotifyLink( url ); return GlobalActionManager::instance()->openSpotifyLink( url );
else else
{ {
QFile f( url ); QFile f( url );