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

Parse Grooveshark playlists on drag & drop

not much error handling yet, but it works ;)
This commit is contained in:
Stefan Derkits 2011-12-21 14:30:24 +01:00
parent 52cb7cebd9
commit e989bc1652
5 changed files with 372 additions and 0 deletions

View File

@ -101,6 +101,7 @@ set( libGuiSources
utils/m3uloader.cpp
utils/itunesparser.cpp
utils/rdioparser.cpp
utils/groovesharkparser.cpp
utils/shortenedlinkparser.cpp
utils/stylehelper.cpp
utils/dropjobnotifier.cpp
@ -218,6 +219,7 @@ set( libGuiHeaders
utils/m3uloader.h
utils/itunesparser.h
utils/rdioparser.h
utils/groovesharkparser.h
utils/shortenedlinkparser.h
utils/dropjobnotifier.h
utils/tomahawkutilsgui.h

View File

@ -26,6 +26,7 @@
#include "utils/spotifyparser.h"
#include "utils/itunesparser.h"
#include "utils/rdioparser.h"
#include "utils/groovesharkparser.h"
#include "utils/m3uloader.h"
#include "utils/shortenedlinkparser.h"
#include "utils/logger.h"
@ -120,6 +121,9 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
// Not the most elegant
if ( url.contains( "spotify" ) && url.contains( "playlist" ) && s_canParseSpotifyPlaylists )
return true;
if ( url.contains( "grooveshark.com" ) && url.contains( "playlist" ) )
return true;
}
if ( acceptedType.testFlag( Track ) )
@ -188,6 +192,9 @@ DropJob::isDropType( DropJob::DropType desired, const QMimeData* data )
if ( url.contains( "rdio.com" ) && url.contains( "people" ) && url.contains( "playlist" ) )
return true;
if ( url.contains( "grooveshark.com" ) && url.contains( "playlist" ) )
return true;
if ( ShortenedLinkParser::handlesUrl( url ) )
return true;
@ -555,6 +562,27 @@ DropJob::handleRdioUrls( const QString& urlsRaw )
rdio->parse( urls );
}
void
DropJob::handleGroovesharkUrls ( const QString& urlsRaw )
{
QStringList urls = urlsRaw.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
tDebug() << "Got Grooveshark urls!" << urls;
if ( dropAction() == Default )
setDropAction( Create );
GroovesharkParser* groove = new GroovesharkParser( urls, dropAction() == Create, this );
connect( groove, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( onTracksAdded( QList< Tomahawk::query_ptr > ) ) );
if ( dropAction() == Append )
{
tDebug() << Q_FUNC_INFO << "Asking for grooveshark contents from" << urls;
connect( groove, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( onTracksAdded( QList< Tomahawk::query_ptr > ) ) );
m_queryCount++;
}
}
void
DropJob::handleAllUrls( const QString& urls )
@ -569,6 +597,8 @@ DropJob::handleAllUrls( const QString& urls )
handleSpotifyUrls( urls );
else if ( urls.contains( "rdio.com" ) )
handleRdioUrls( urls );
else if ( urls.contains( "grooveshark.com" ) )
handleGroovesharkUrls( urls );
else
handleTrackUrls ( urls );
}

View File

@ -106,6 +106,7 @@ public:
void handleM3u( const QString& urls );
void handleSpotifyUrls( const QString& urls );
void handleRdioUrls( const QString& urls );
void handleGroovesharkUrls( const QString& urls );
static bool canParseSpotifyPlaylists() { return s_canParseSpotifyPlaylists; }
static void setCanParseSpotifyPlaylists( bool parseable ) { s_canParseSpotifyPlaylists = parseable; }

View File

@ -0,0 +1,258 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Stefan Derkits <stefan@derkits.at>
*
* 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 "groovesharkparser.h"
#include "utils/logger.h"
#include "utils/tomahawkutils.h"
#include "query.h"
#include "sourcelist.h"
#include "dropjob.h"
#include "jobview/JobStatusView.h"
#include "jobview/JobStatusModel.h"
#include "dropjobnotifier.h"
#include "viewmanager.h"
#include <qjson/parser.h>
#include <QtCrypto>
#include <QCoreApplication>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
using namespace Tomahawk;
QPixmap* GroovesharkParser::s_pixmap = 0;
const char* enApiSecret = "erCj5s0Vebyqtc9Aduyotc1CLListJ9HfO2os5hBeew=";
GroovesharkParser::GroovesharkParser( const QStringList& trackUrls, bool createNewPlaylist, QObject* parent )
: QObject ( parent )
, m_limit ( 40 )
, m_trackMode( true )
, m_createNewPlaylist( createNewPlaylist )
, m_browseJob( 0 )
{
foreach ( const QString& url, trackUrls )
lookupUrl( url );
}
GroovesharkParser::~GroovesharkParser()
{
}
void
GroovesharkParser::lookupUrl( const QString& link )
{
if( link.contains( "playlist" ) )
{
if( !m_createNewPlaylist )
m_trackMode = true;
else
m_trackMode = false;
lookupGroovesharkPlaylist( link );
}
else
return;
}
void
GroovesharkParser::lookupGroovesharkPlaylist( const QString& linkRaw )
{
tLog() << "Parsing Grooveshark Playlist URI:" << linkRaw;
QString browseUri = linkRaw;
QStringList urlParts = linkRaw.split("/");
bool ok;
int playlistID = urlParts.last().toInt(&ok, 10);
if (!ok)
{
tDebug() << "incorrect grooveshark url";
return;
}
m_title = urlParts.at(urlParts.size()-2);
tDebug() << "should get playlist " << playlistID;
DropJob::DropType type;
if ( browseUri.contains( "playlist" ) )
type = DropJob::Playlist;
QString base_url("http://api.grooveshark.com/ws3.php?sig=");
QByteArray data = QString("{\"method\":\"getPlaylistSongs\",\"parameters\":{\"playlistID\":\"%1\"},\"header\":{\"wsKey\":\"tomahawkplayer\"}}").arg(playlistID).toLocal8Bit();
QByteArray magic = QByteArray::fromBase64( enApiSecret );
QByteArray wand = QByteArray::fromBase64( QCoreApplication::applicationName().toLatin1() );
int length = magic.length(), n2 = wand.length();
for ( int i=0; i<length; i++ ) magic[i] = magic[i] ^ wand[i%n2];
QCA::SymmetricKey key( magic );
tDebug() << "key size: " << key.size();
QCA::MessageAuthenticationCode hmac(QString("hmac(md5)"),key);
tDebug() << "created hmac object";
QCA::SecureArray secdata( data );
tDebug() << "before creating hmac";
hmac.update(secdata);
QCA::SecureArray resultArray = hmac.final();
QString hash = QCA::arrayToHex(resultArray.toByteArray());
QUrl url = QUrl(base_url + hash);
tDebug() << "Looking up URL..." << url.toString();
QNetworkReply* reply = TomahawkUtils::nam()->post( QNetworkRequest( url ), data );
connect( reply, SIGNAL( finished() ), this, SLOT( groovesharkLookupFinished() ) );
//m_browseJob = new DropJobNotifier( pixmap(), "Grooveshark", type, reply );
//JobStatusView::instance()->model()->addJob( m_browseJob );
m_queries.insert( reply );
}
void
GroovesharkParser::groovesharkLookupFinished()
{
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 Grooveshark browse item :" << p.errorString() << "On line" << p.errorLine();
checkTrackFinished();
return;
}
//tDebug() << "parsed grooveshark result into a map " << res;
//tDebug() << "results " << res.value("result") << " which is of type " << res.value("result").type();
QVariantList list = res.value("result").toMap().value("songs").toList();
foreach (const QVariant& var, list)
{
QVariantMap trackResult = var.toMap();
//tDebug() << map;
QString title, artist, album;
title = trackResult.value( "SongName", QString() ).toString();
artist = trackResult.value( "ArtistName", QString() ).toString();
album = trackResult.value( "AlbumName", 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(), m_trackMode );
m_tracks << q;
}
} else
{
tLog() << "Error in network request to grooveshark for track decoding:" << r->errorString();
}
if ( m_trackMode )
checkTrackFinished();
else
checkBrowseFinished();
}
void
GroovesharkParser::checkBrowseFinished()
{
tDebug() << "Checking for grooveshark batch playlist job finished" << m_queries.isEmpty() << m_createNewPlaylist;
if ( m_queries.isEmpty() ) // we're done
{
if ( m_browseJob )
m_browseJob->setFinished();
if( m_createNewPlaylist && !m_tracks.isEmpty() )
{
m_playlist = Playlist::create( SourceList::instance()->getLocal(),
uuid(),
m_title,
m_info,
m_creator,
false,
m_tracks );
connect( m_playlist.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( playlistCreated() ) );
return;
}
emit tracks( m_tracks );
deleteLater();
}
}
void
GroovesharkParser::checkTrackFinished()
{
tDebug() << "Checking for grooveshark batch track job finished" << m_queries.isEmpty();
if ( m_queries.isEmpty() ) // we're done
{
if ( m_browseJob )
m_browseJob->setFinished();
emit tracks( m_tracks );
deleteLater();
}
}
void
GroovesharkParser::playlistCreated()
{
ViewManager::instance()->show( m_playlist );
deleteLater();
}
QPixmap
GroovesharkParser::pixmap() const
{
if ( !s_pixmap )
s_pixmap = new QPixmap( RESPATH "images/spotify-logo.png" );
return *s_pixmap;
}

View File

@ -0,0 +1,81 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Stefan Derkits <stefan@derkits.at>
*
* 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 GROOVESHARKPARSER_H
#define GROOVESHARKPARSER_H
#include "dllmacro.h"
#include "typedefs.h"
#include "query.h"
#include "jobview/JobStatusItem.h"
#include <QObject>
#include <QSet>
#include <QtCore/QStringList>
/**
* Small class to parse grooveshark links into query_ptrs
*
* Connect to the signals to get the results
*/
class QNetworkReply;
namespace Tomahawk
{
class DropJobNotifier;
class DLLEXPORT GroovesharkParser : public QObject
{
Q_OBJECT
public:
explicit GroovesharkParser( const QStringList& trackUrls, bool createNewPlaylist = false, QObject* parent = 0 );
virtual ~GroovesharkParser();
signals:
void track( const Tomahawk::query_ptr& track );
void tracks( const QList< Tomahawk::query_ptr > tracks );
void playlist( const Tomahawk::query_ptr& playlist );
private slots:
void groovesharkLookupFinished();
void playlistCreated();
private:
QPixmap pixmap() const;
void lookupUrl( const QString& url );
void lookupGroovesharkPlaylist( const QString& playlist );
void checkTrackFinished();
void checkBrowseFinished();
int m_limit;
bool m_trackMode;
bool m_createNewPlaylist;
QList< query_ptr > m_tracks;
QSet< QNetworkReply* > m_queries;
QString m_title, m_info, m_creator;
Tomahawk::playlist_ptr m_playlist;
DropJobNotifier* m_browseJob;
static QPixmap* s_pixmap;
};
}
#endif // GROOVESHARKPARSER_H