mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-23 06:02:53 +02:00
* Updated liblastfm2 to latest snapshot.
This commit is contained in:
@@ -818,19 +818,21 @@ LastFmPlugin::createScrobbler()
|
||||
|
||||
|
||||
QList<lastfm::Track>
|
||||
LastFmPlugin::parseTrackList( QNetworkReply * reply )
|
||||
LastFmPlugin::parseTrackList( QNetworkReply* reply )
|
||||
{
|
||||
QList<lastfm::Track> tracks;
|
||||
try {
|
||||
lastfm::XmlQuery lfm = lastfm::ws::parse(reply);
|
||||
foreach (lastfm::XmlQuery xq, lfm.children( "track" )) {
|
||||
lastfm::XmlQuery lfm = reply->readAll();
|
||||
foreach ( lastfm::XmlQuery xq, lfm.children( "track" ) )
|
||||
{
|
||||
tracks.append( lastfm::Track( xq ) );
|
||||
}
|
||||
}
|
||||
catch (lastfm::ws::ParseError& e)
|
||||
catch( lastfm::ws::ParseError& e )
|
||||
{
|
||||
qWarning() << e.what();
|
||||
}
|
||||
|
||||
return tracks;
|
||||
}
|
||||
|
||||
|
8
thirdparty/liblastfm2/admin/utils.rb
vendored
8
thirdparty/liblastfm2/admin/utils.rb
vendored
@@ -1,13 +1,7 @@
|
||||
cwd = File.dirname( __FILE__ )
|
||||
require "#{cwd}/platform.rb"
|
||||
|
||||
def h(s, n)
|
||||
case Platform::IMPL
|
||||
when :mswin
|
||||
puts '==> '+s
|
||||
else
|
||||
puts "\033[0;#{n}m==>\033[0;0;1m #{s} \033[0;0m"
|
||||
end
|
||||
end
|
||||
|
||||
def h1 s
|
||||
@@ -37,4 +31,4 @@ def pkgconfig pkg, prettyname
|
||||
system "pkg-config --exists '#{pkg}'"
|
||||
raise PkgConfigNotFound if $? == 127
|
||||
raise PkgNotFound.new(prettyname) if $? != 0
|
||||
end
|
||||
end
|
||||
|
93
thirdparty/liblastfm2/configure
vendored
93
thirdparty/liblastfm2/configure
vendored
@@ -1,32 +1,8 @@
|
||||
#!/usr/bin/ruby
|
||||
if ARGV.include? '--help'
|
||||
puts "usage: ./configure [--prefix <dir>] [--release] [--no-strip] [--skip-checks]"
|
||||
exit
|
||||
end
|
||||
|
||||
cwd = File.dirname( __FILE__ )
|
||||
require "#{cwd}/admin/platform.rb"
|
||||
require "#{cwd}/admin/which_qmake.rb"
|
||||
require "#{cwd}/admin/utils.rb"
|
||||
|
||||
begin
|
||||
IO.read("#{cwd}/src/global.h") =~ /LASTFM_VERSION_STRING\s+"((\d\.)*\d)"/
|
||||
abort "Couldn't determine our version!" if $1.nil?
|
||||
LFM_VERSION=$1
|
||||
ENV['LFM_VERSION']=LFM_VERSION
|
||||
|
||||
h1 "Configuring liblastfm-#{LFM_VERSION}..."
|
||||
|
||||
unless ARGV.include? '--skip-checks'
|
||||
$qmake=which_qmake
|
||||
pkgconfig 'samplerate', 'libsamplerate'
|
||||
pkgconfig 'fftw3f', 'fftw'
|
||||
puts 'Using '+`which #{$qmake}` unless Platform::IMPL == :mswin
|
||||
else
|
||||
$qmake='qmake'
|
||||
end
|
||||
|
||||
h2 'Determining installation prefix' do
|
||||
h2 'Determining installation prefix' do
|
||||
if ARGV.include? '--prefix'
|
||||
n=ARGV.index '--prefix'
|
||||
ENV['LFM_PREFIX'] = ARGV[n+1]
|
||||
@@ -38,34 +14,10 @@ begin
|
||||
puts "Will install to: "+ENV['LFM_PREFIX']
|
||||
end
|
||||
|
||||
h1 'Generating Build System'
|
||||
|
||||
h2 'Generating .qmake.env' do
|
||||
f = File.new("#{cwd}/.qmake.env", 'w')
|
||||
f.write qmake_env('CC', 'QMAKE_CC')
|
||||
f.write qmake_env('CXX', 'QMAKE_CXX')
|
||||
f.write qmake_env('LDFLAGS', 'QMAKE_LFLAGS_RELEASE')
|
||||
f.write qmake_env(['CFLAGS', 'CPPFLAGS'], 'QMAKE_CFLAGS_RELEASE')
|
||||
f.write qmake_env(['CXXFLAGS', 'CPPFLAGS'], 'QMAKE_CXXFLAGS_RELEASE')
|
||||
f.close
|
||||
end unless Platform::IMPL == :mswin
|
||||
|
||||
h2 "Running qpp..." do
|
||||
['src/lastfm.pro','src/fingerprint/fingerprint.pro'].each do |p|
|
||||
d="#{cwd}/#{File.dirname p}"
|
||||
f=File.new "#{d}/_files.qmake", 'w'
|
||||
f.write `ruby admin/qpp #{p}`
|
||||
# on Windows VERSION produces lastfm0.dll, the 0 breaks the build
|
||||
f.puts "VERSION = #{LFM_VERSION}" unless Platform::OS == :win32
|
||||
end
|
||||
end
|
||||
|
||||
h2 "Configuring qmake..." do
|
||||
args=Array.new
|
||||
args << '-spec macx-g++' if Platform::IMPL == :macosx
|
||||
if ARGV.include? '--release'
|
||||
args << '-config release'
|
||||
args << '"CONFIG += app_bundle"' if Platform::IMPL == :macosx and ARGV.include? '--bundle'
|
||||
else
|
||||
args << '-config debug'
|
||||
end
|
||||
@@ -82,46 +34,13 @@ begin
|
||||
hs << 'fingerprint/Fingerprint.h' << 'fingerprint/FingerprintableSource.h'
|
||||
hs << 'radio/RadioStation.h' << 'radio/RadioTuner.h'
|
||||
hs << 'scrobble/Audioscrobbler.h' << 'scrobble/ScrobblePoint.h' << 'scrobble/ScrobbleCache.h'
|
||||
hs << 'types/AbstractType.h' << 'types/Track.h' << 'types/Mbid.h' << 'types/Artist.h' << 'types/Album.h' << 'types/FingerprintId.h' << 'types/Playlist.h' << 'types/Tag.h' << 'types/User.h' << 'types/Xspf.h'
|
||||
hs << 'types/Tasteometer.h' << 'types/AbstractType.h' << 'types/Track.h' << 'types/Mbid.h' << 'types/Artist.h' << 'types/Album.h' << 'types/FingerprintId.h' << 'types/Playlist.h' << 'types/Tag.h' << 'types/User.h' << 'types/Xspf.h'
|
||||
hs << 'ws/ws.h' << 'ws/InternetConnectionMonitor.h' << 'ws/NetworkAccessManager.h'
|
||||
|
||||
File.new("#{cwd}/Makefile", 'w').write `ruby admin/Makefile.rb #{hs.join(' ')}`
|
||||
end
|
||||
|
||||
case Platform::IMPL
|
||||
when :mswin then make='nmake all'
|
||||
else make='make' # NOTE only tested with GNU make, sorry :(
|
||||
uname = `uname`
|
||||
ENV['LFM_VERSION']="0.4.0"
|
||||
File.new("#{cwd}/Makefile", 'w').write `ruby admin/Makefile.rb #{hs.join(' ')}`
|
||||
end
|
||||
|
||||
puts
|
||||
puts "Good, your configure is finished! Now type: #{make}"
|
||||
puts "Good, your configure is finished! Now type: make"
|
||||
|
||||
rescue QMakeTooOld
|
||||
puts <<-sput
|
||||
|
||||
Your version of Qt seems to be too old, we require Qt 4.4 or above.
|
||||
|
||||
It is possible you have Qt3 and Qt4 both installed. Locate your Qt4
|
||||
installation and ensure it is placed first in the path, eg:
|
||||
|
||||
PATH=/opt/qt4/bin:\$PATH ./configure
|
||||
|
||||
sput
|
||||
exit 1
|
||||
rescue QMakeNotFound
|
||||
puts "Sorry, qmake was not found, is Qt4 installed?"
|
||||
exit 2
|
||||
rescue PkgNotFound => e
|
||||
puts <<-sput
|
||||
|
||||
Sorry, we couldn't find #{e}.
|
||||
You can try to compile anyway by forcing configure to finish:
|
||||
|
||||
./configure --skip-checks
|
||||
|
||||
sput
|
||||
exit 3
|
||||
rescue PkgConfigNotFound
|
||||
puts "Sorry, pkg-config could not be found. You should install it!"
|
||||
exit 4
|
||||
end
|
||||
|
@@ -36,7 +36,7 @@ lastfm::UrlBuilder::url() const
|
||||
QByteArray //static
|
||||
lastfm::UrlBuilder::encode( QString s )
|
||||
{
|
||||
foreach (QChar c, QList<QChar>() << '&' << '/' << ';' << '+' << '#' << '%')
|
||||
foreach (QChar c, QList<QChar>() << '%' << '&' << '/' << ';' << '+' << '#' << '"')
|
||||
if (s.contains( c ))
|
||||
// the middle step may seem odd but this is what the site does
|
||||
// eg. search for the exact string "Radiohead 2 + 2 = 5"
|
||||
|
49
thirdparty/liblastfm2/src/core/XmlQuery.cpp
vendored
49
thirdparty/liblastfm2/src/core/XmlQuery.cpp
vendored
@@ -18,14 +18,55 @@
|
||||
along with liblastfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "XmlQuery.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QStringList>
|
||||
|
||||
using lastfm::XmlQuery;
|
||||
|
||||
|
||||
XmlQuery::XmlQuery( const QByteArray& bytes )
|
||||
{
|
||||
domdoc.setContent(bytes);
|
||||
e = domdoc.documentElement();
|
||||
XmlQuery::XmlQuery( const QByteArray& bytes ) throw( lastfm::ws::ParseError )
|
||||
{
|
||||
try
|
||||
{
|
||||
if ( !bytes.size() )
|
||||
throw lastfm::ws::ParseError( lastfm::ws::MalformedResponse, "No data" );
|
||||
|
||||
if( !domdoc.setContent( bytes ) )
|
||||
throw lastfm::ws::ParseError( lastfm::ws::MalformedResponse, "Invalid XML" );
|
||||
|
||||
e = domdoc.documentElement();
|
||||
|
||||
if (e.isNull())
|
||||
throw lastfm::ws::ParseError( lastfm::ws::MalformedResponse, "Lfm is null" );
|
||||
|
||||
QString const status = e.attribute( "status" );
|
||||
QDomElement error = e.firstChildElement( "error" );
|
||||
uint const n = e.childNodes().count();
|
||||
|
||||
// no elements beyond the lfm is perfectably acceptable <-- wtf?
|
||||
// if (n == 0) // nothing useful in the response
|
||||
if (status == "failed" || (n == 1 && !error.isNull()) )
|
||||
throw error.isNull()
|
||||
? lastfm::ws::ParseError( lastfm::ws::MalformedResponse, "" )
|
||||
: lastfm::ws::ParseError( lastfm::ws::Error( error.attribute( "code" ).toUInt() ), error.text() );
|
||||
|
||||
}
|
||||
catch ( lastfm::ws::ParseError e )
|
||||
{
|
||||
switch ( e.enumValue() )
|
||||
{
|
||||
case lastfm::ws::OperationFailed:
|
||||
case lastfm::ws::InvalidApiKey:
|
||||
case lastfm::ws::InvalidSessionKey:
|
||||
// NOTE will never be received during the LoginDialog stage
|
||||
// since that happens before this slot is registered with
|
||||
// QMetaObject in App::App(). Neat :)
|
||||
QMetaObject::invokeMethod( qApp, "onWsError", Q_ARG( lastfm::ws::Error, e.enumValue() ) );
|
||||
default:
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
3
thirdparty/liblastfm2/src/core/XmlQuery.h
vendored
3
thirdparty/liblastfm2/src/core/XmlQuery.h
vendored
@@ -21,6 +21,7 @@
|
||||
#define LASTFM_XMLQUERY_H
|
||||
|
||||
#include <lastfm/global.h>
|
||||
#include <lastfm/ws.h>
|
||||
#include <QDomDocument>
|
||||
#include <QDomElement>
|
||||
|
||||
@@ -44,7 +45,7 @@ namespace lastfm
|
||||
* Notice the lfm node is not referenced, that is because it is the
|
||||
* document-element of the XML document.
|
||||
*/
|
||||
XmlQuery( const QByteArray& );
|
||||
XmlQuery( const QByteArray& ) throw( lastfm::ws::ParseError );
|
||||
|
||||
XmlQuery( const QDomElement& e, const char* name = "" ) : e( e )
|
||||
{
|
||||
|
33
thirdparty/liblastfm2/src/core/misc.cpp
vendored
33
thirdparty/liblastfm2/src/core/misc.cpp
vendored
@@ -60,7 +60,38 @@ static QDir dataDotDot()
|
||||
return QDir::home();
|
||||
|
||||
#elif defined(Q_WS_MAC)
|
||||
return QDir::home().filePath( "Library/Application Support" );
|
||||
|
||||
#define EIT( x ) { OSErr err = x; if (err != noErr) throw 1; }
|
||||
try
|
||||
{
|
||||
short vRefNum = 0;
|
||||
long dirId;
|
||||
EIT( ::FindFolder( kOnAppropriateDisk,
|
||||
kApplicationSupportFolderType,
|
||||
kDontCreateFolder,
|
||||
&vRefNum,
|
||||
&dirId ) );
|
||||
|
||||
// Now we have a vRefNum and a dirID - but *not* an Unix-Path as string.
|
||||
// Lets make one based from this:
|
||||
FSSpec fsspec;
|
||||
EIT( ::FSMakeFSSpec( vRefNum, dirId, NULL, &fsspec ) );
|
||||
|
||||
// ...and build an FSRef based on thes FSSpec.
|
||||
FSRef fsref;
|
||||
EIT( ::FSpMakeFSRef( &fsspec, &fsref ) );
|
||||
|
||||
// ...then extract the Unix Path as a C-String from the FSRef
|
||||
unsigned char path[512];
|
||||
EIT( ::FSRefMakePath( &fsref, path, 512 ) );
|
||||
|
||||
return QDir::homePath() + QString::fromUtf8( (char*)path );
|
||||
}
|
||||
catch (int)
|
||||
{
|
||||
return QDir::home().filePath( "Library/Application Support" );
|
||||
}
|
||||
|
||||
#elif defined(Q_WS_X11)
|
||||
return QDir::home().filePath( ".local/share" );
|
||||
|
||||
|
69
thirdparty/liblastfm2/src/lastfm.pro
vendored
69
thirdparty/liblastfm2/src/lastfm.pro
vendored
@@ -1,6 +1,5 @@
|
||||
TEMPLATE = lib
|
||||
QT = core network xml
|
||||
include( _files.qmake )
|
||||
|
||||
INSTALLS = target
|
||||
target.path = /lib
|
||||
@@ -18,3 +17,71 @@ mac{
|
||||
linux*{
|
||||
QT += dbus
|
||||
}
|
||||
|
||||
SOURCES += \
|
||||
ws/ws.cpp \
|
||||
ws/NetworkConnectionMonitor.cpp \
|
||||
ws/NetworkAccessManager.cpp \
|
||||
ws/InternetConnectionMonitor.cpp \
|
||||
types/Xspf.cpp \
|
||||
types/User.cpp \
|
||||
types/Track.cpp \
|
||||
types/Tasteometer.cpp \
|
||||
types/Tag.cpp \
|
||||
types/Playlist.cpp \
|
||||
types/Mbid.cpp \
|
||||
types/FingerprintId.cpp \
|
||||
types/Artist.cpp \
|
||||
types/Album.cpp \
|
||||
scrobble/ScrobbleCache.cpp \
|
||||
scrobble/Audioscrobbler.cpp \
|
||||
radio/RadioTuner.cpp \
|
||||
radio/RadioStation.cpp \
|
||||
core/XmlQuery.cpp \
|
||||
core/UrlBuilder.cpp \
|
||||
core/misc.cpp
|
||||
|
||||
HEADERS += \
|
||||
ws/ws.h \
|
||||
ws/NetworkConnectionMonitor.h \
|
||||
ws/NetworkAccessManager.h \
|
||||
ws/InternetConnectionMonitor.h \
|
||||
types/Xspf.h \
|
||||
types/User.h \
|
||||
types/Track.h \
|
||||
types/Tasteometer.h \
|
||||
types/Tag.h \
|
||||
types/Playlist.h \
|
||||
types/Mbid.h \
|
||||
types/FingerprintId.h \
|
||||
types/Artist.h \
|
||||
types/Album.h \
|
||||
types/AbstractType.h \
|
||||
scrobble/ScrobblePoint.h \
|
||||
scrobble/ScrobbleCache.h \
|
||||
scrobble/Audioscrobbler.h \
|
||||
radio/RadioTuner.h \
|
||||
radio/RadioStation.h \
|
||||
global.h \
|
||||
core/XmlQuery.h \
|
||||
core/UrlBuilder.h \
|
||||
core/misc.h
|
||||
|
||||
win32:SOURCES += ws/win/WNetworkConnectionMonitor_win.cpp \
|
||||
ws/win/WmiSink.cpp \
|
||||
ws/win/Pac.cpp \
|
||||
ws/win/NdisEvents.cpp
|
||||
|
||||
win32:HEADERS += ws/win/WNetworkConnectionMonitor.h \
|
||||
ws/win/WmiSink.h \
|
||||
ws/win/Pac.h \
|
||||
ws/win/NdisEvents.h \
|
||||
ws/win/IeSettings.h \
|
||||
ws/win/ComSetup.h
|
||||
|
||||
mac:SOURCES += ws/mac/MNetworkConnectionMonitor_mac.cpp
|
||||
|
||||
mac:HEADERS += ws/mac/ProxyDict.h \
|
||||
ws/mac/MNetworkConnectionMonitor.h
|
||||
|
||||
!win32:VERSION = 0.4.0
|
||||
|
380
thirdparty/liblastfm2/src/radio/RadioStation.cpp
vendored
380
thirdparty/liblastfm2/src/radio/RadioStation.cpp
vendored
@@ -23,22 +23,190 @@
|
||||
#include "RadioStation.h"
|
||||
#include "../core/XmlQuery.h"
|
||||
|
||||
QRegExp rxDisco("opt:discovery\\|(\\S+)", Qt::CaseSensitive, QRegExp::RegExp2);
|
||||
QRegExp rxRep("opt:rep\\|([\\d\\.]+)", Qt::CaseSensitive, QRegExp::RegExp2);
|
||||
QRegExp rxMainstr("opt:mainstr\\|([\\d\\.]+)", Qt::CaseSensitive, QRegExp::RegExp2);
|
||||
|
||||
|
||||
const float k_defaultRep(0.5);
|
||||
const float k_defaultMainstr(0.5);
|
||||
const bool k_defaultDisco(false);
|
||||
|
||||
lastfm::RadioStation
|
||||
lastfm::RadioStation::library( const lastfm::User& user )
|
||||
{
|
||||
QList<lastfm::User> users;
|
||||
users << user;
|
||||
return library( users );
|
||||
}
|
||||
|
||||
lastfm::RadioStation
|
||||
lastfm::RadioStation::library( QList<lastfm::User>& users )
|
||||
{
|
||||
RadioStation s( libraryStr( users ) );
|
||||
if( users.count() == 1 )
|
||||
s.setTitle( QObject::tr( "%1%2s Library Radio").arg( lastfm::ws::Username, QChar(0x2019) ));
|
||||
|
||||
else {
|
||||
QString title;
|
||||
for( QList<lastfm::User>::const_iterator i = users.begin(); i != users.end(); i++ ) {
|
||||
if( i == users.end() - 1 )
|
||||
title += " and " + *i;
|
||||
else
|
||||
title += ", " + *i;
|
||||
}
|
||||
|
||||
s.setTitle( title );
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
lastfm::RadioStation
|
||||
lastfm::RadioStation::recommendations( const lastfm::User& user )
|
||||
{
|
||||
RadioStation s( recommendationsStr( user ) );
|
||||
|
||||
s.setTitle( QObject::tr( "%1%2s Recommended Radio").arg( lastfm::ws::Username, QChar(0x2019) ));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
lastfm::RadioStation
|
||||
lastfm::RadioStation::friends( const lastfm::User& user )
|
||||
{
|
||||
RadioStation s( friendsStr( user ) );
|
||||
|
||||
s.setTitle( QObject::tr( "%1%2s Friends Radio").arg( lastfm::ws::Username, QChar(0x2019) ));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
lastfm::RadioStation
|
||||
lastfm::RadioStation::neighbourhood( const lastfm::User& user )
|
||||
{
|
||||
RadioStation s( neighbourhoodStr( user ) );
|
||||
s.setTitle( QObject::tr( "%1%2s Neighbours%2 Radio").arg( lastfm::ws::Username, QChar(0x2019) ));
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
lastfm::RadioStation
|
||||
lastfm::RadioStation::tag( const lastfm::Tag& tag )
|
||||
{
|
||||
QList<lastfm::Tag> tags;
|
||||
tags << tag;
|
||||
return lastfm::RadioStation::tag( tags );
|
||||
}
|
||||
|
||||
|
||||
lastfm::RadioStation
|
||||
lastfm::RadioStation::tag( QList<lastfm::Tag>& tag )
|
||||
{
|
||||
return RadioStation( tagStr( tag ) );
|
||||
}
|
||||
|
||||
|
||||
lastfm::RadioStation
|
||||
lastfm::RadioStation::similar( const lastfm::Artist& artist )
|
||||
{
|
||||
QList<lastfm::Artist> artists;
|
||||
artists << artist;
|
||||
return similar( artists );
|
||||
}
|
||||
|
||||
|
||||
lastfm::RadioStation
|
||||
lastfm::RadioStation::similar( QList<lastfm::Artist>& artists )
|
||||
{
|
||||
return RadioStation( similarStr( artists ) );
|
||||
}
|
||||
|
||||
|
||||
lastfm::RadioStation
|
||||
lastfm::RadioStation::mix( const lastfm::User& user )
|
||||
{
|
||||
RadioStation s( mixStr( user ) );
|
||||
s.setTitle( QObject::tr( "%1%2s Mix Radio").arg( lastfm::ws::Username, QChar(0x2019) ) );
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
QString
|
||||
lastfm::RadioStation::url() const
|
||||
{
|
||||
return m_url.toString() + (m_tagFilter.isEmpty() ? "" : "/tag/" + m_tagFilter);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lastfm::RadioStation::setTitle( const QString& s )
|
||||
{
|
||||
// Stop the radio station getting renamed when the web services don't know what it's called
|
||||
if ( !m_title.isEmpty() && s.compare( "a radio station", Qt::CaseInsensitive ) == 0 )
|
||||
return;
|
||||
|
||||
QString title = s.trimmed();
|
||||
|
||||
if ( title.compare( QObject::tr("%1%2s Library Radio").arg( lastfm::ws::Username, QChar(0x2019) ), Qt::CaseInsensitive ) == 0 )
|
||||
title = QObject::tr("My Library Radio");
|
||||
else if ( title.compare( QObject::tr("%1%2s Mix Radio").arg( lastfm::ws::Username, QChar(0x2019) ), Qt::CaseInsensitive ) == 0 )
|
||||
title = QObject::tr("My Mix Radio");
|
||||
else if ( title.compare( QObject::tr("%1%2s Recommended Radio").arg( lastfm::ws::Username, QChar(0x2019) ), Qt::CaseInsensitive ) == 0 )
|
||||
title = QObject::tr("My Recommended Radio");
|
||||
else if ( title.compare( QObject::tr("%1%2s Friends%2 Radio").arg( lastfm::ws::Username, QChar(0x2019) ), Qt::CaseInsensitive ) == 0 )
|
||||
title = QObject::tr("My Friends%1 Radio").arg( QChar( 0x2019 ) );
|
||||
else if ( title.compare( QObject::tr("%1%2s Friends Radio").arg( lastfm::ws::Username, QChar(0x2019) ), Qt::CaseInsensitive ) == 0 )
|
||||
title = QObject::tr("My Friends%1 Radio").arg( QChar( 0x2019 ) );
|
||||
else if ( title.compare( QObject::tr("%1%2s Neighbours%2 Radio").arg( lastfm::ws::Username, QChar(0x2019) ), Qt::CaseInsensitive ) == 0 )
|
||||
title = QObject::tr("My Neighbours%1 Radio").arg( QChar( 0x2019 ) );
|
||||
else if ( title.compare( QObject::tr("%1%2s Neighbours Radio").arg( lastfm::ws::Username ), Qt::CaseInsensitive ) == 0 )
|
||||
title = QObject::tr("My Neighbours%1 Radio").arg( QChar( 0x2019 ) );
|
||||
|
||||
m_title = title;
|
||||
}
|
||||
|
||||
|
||||
QString
|
||||
lastfm::RadioStation::title() const
|
||||
{
|
||||
return m_title; // + (m_tagFilter.isEmpty() ? "" : ": " + m_tagFilter);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lastfm::RadioStation::setTagFilter( const QString& tag )
|
||||
{
|
||||
m_tagFilter = tag;
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply*
|
||||
lastfm::RadioStation::getSampleArtists( int limit ) const
|
||||
{
|
||||
QMap<QString, QString> map;
|
||||
map["method"] = "radio.getSampleArtists";
|
||||
map["station"] = m_url.toString();
|
||||
map["limit"] = QString::number( limit );
|
||||
return ws::get( map );
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply*
|
||||
lastfm::RadioStation::getTagSuggestions( int limit ) const
|
||||
{
|
||||
QMap<QString, QString> map;
|
||||
map["method"] = "radio.getTagSuggestions";
|
||||
map["station"] = m_url.toString();
|
||||
map["limit"] = QString::number( limit );
|
||||
return ws::get( map );
|
||||
}
|
||||
|
||||
|
||||
//static
|
||||
QList<lastfm::RadioStation>
|
||||
lastfm::RadioStation::list( QNetworkReply* r )
|
||||
{
|
||||
QList<lastfm::RadioStation> result;
|
||||
try {
|
||||
foreach (XmlQuery xq, XmlQuery(ws::parse(r)).children("station")) {
|
||||
foreach (XmlQuery xq, XmlQuery( r->readAll() ).children("station")) {
|
||||
lastfm::RadioStation rs( QUrl::fromPercentEncoding( xq["url"].text().toUtf8() ) );
|
||||
rs.setTitle(xq["name"].text());
|
||||
result.append(rs);
|
||||
@@ -51,151 +219,119 @@ lastfm::RadioStation::list( QNetworkReply* r )
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
lastfm::RadioStation::operator==( const RadioStation& that ) const
|
||||
{
|
||||
return this->m_url == that.m_url && this->m_tagFilter == that.m_tagFilter;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lastfm::RadioStation::setString( const QString& string )
|
||||
{
|
||||
QString replaceString( string );
|
||||
QString decodedString = QUrl::fromPercentEncoding( replaceString.replace( QChar('+'), QChar(' ') ).toUtf8() );
|
||||
// If it's a tag filtered station then extract that part
|
||||
QString tempString = string;
|
||||
|
||||
QRegExp rxRql( "lastfm:\\/\\/rql\\/(.+)$" );
|
||||
QRegExp rxPersonal( "lastfm:\\/\\/user\\/(.+)\\/personal" );
|
||||
QRegExp rxRecommended( "lastfm://user/(.+)\\/recommended" );
|
||||
QRegExp rxNeighbours( "lastfm:\\/\\/user\\/(.+)\\/neighbours" );
|
||||
QRegExp rxLoved( "lastfm:\\/\\/user\\/(.+)\\/loved" );
|
||||
QRegExp rxGlobalTags( "lastfm:\\/\\/globaltags\\/(.+)" );
|
||||
QRegExp rxSimilarArtists( "lastfm:\\/\\/artist\\/(.+)\\/similarartists" );
|
||||
QRegExp rxUserTags( "lastfm:\\/\\/usertags\\/(.+)\\/(.+)" );
|
||||
QRegExp rxPlaylist( "lastfm:\\/\\/playlist/(.+)\\/shuffle" );
|
||||
|
||||
if (rxRql.indexIn(decodedString) == 0)
|
||||
setRql( QByteArray::fromBase64( rxRql.capturedTexts()[1].toAscii() ) );
|
||||
else if (rxPersonal.indexIn(decodedString) == 0)
|
||||
setRql( libraryStr( rxPersonal.capturedTexts()[1] ) );
|
||||
else if ( rxRecommended.indexIn(decodedString) == 0)
|
||||
setRql( recommendationsStr( rxRecommended.capturedTexts()[1] ) );
|
||||
else if ( rxNeighbours.indexIn(decodedString) == 0)
|
||||
setRql( neighbourhoodStr( rxNeighbours.capturedTexts()[1] ) );
|
||||
else if ( rxLoved.indexIn(decodedString) == 0)
|
||||
setRql( lovedTracksStr( rxLoved.capturedTexts()[1] ) );
|
||||
else if ( rxGlobalTags.indexIn(decodedString) == 0)
|
||||
setRql( globalTagStr( rxGlobalTags.capturedTexts()[1] ) );
|
||||
else if ( rxSimilarArtists.indexIn(decodedString) == 0)
|
||||
setRql( similarStr( rxSimilarArtists.capturedTexts()[1] ) );
|
||||
else if ( rxUserTags.indexIn(decodedString) == 0)
|
||||
setRql( userTagStr( rxUserTags.capturedTexts()[1], rxUserTags.capturedTexts()[2] ) );
|
||||
else if ( rxPlaylist.indexIn(decodedString) == 0)
|
||||
setRql( playlistStr( rxPlaylist.capturedTexts()[1].toInt() ) );
|
||||
else
|
||||
if ( !tempString.startsWith("lastfm://tag/") )
|
||||
{
|
||||
m_url = string;
|
||||
int index = tempString.indexOf("/tag/");
|
||||
|
||||
if ( index != -1 )
|
||||
{
|
||||
m_tagFilter = tempString.mid( index + 5, tempString.count() - (index + 5) );
|
||||
tempString = tempString.mid( 0, index );
|
||||
}
|
||||
}
|
||||
|
||||
m_url = tempString;
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
void
|
||||
lastfm::RadioStation::setRep(float rep)
|
||||
{
|
||||
if ( m_rql.isEmpty() )
|
||||
return false;
|
||||
|
||||
int indexIn = rxRep.indexIn(m_rql);
|
||||
|
||||
if ( indexIn != -1 )
|
||||
{
|
||||
if (rep != k_defaultRep)
|
||||
m_rql.replace( indexIn, rxRep.capturedTexts()[0].length(), QString("opt:rep|%1").arg(rep) );
|
||||
else
|
||||
m_rql.replace( indexIn, rxRep.capturedTexts()[0].length(), "" );
|
||||
}
|
||||
else
|
||||
{
|
||||
// the rql doesn't have rep in it
|
||||
// so append it to the end
|
||||
if (rep != k_defaultRep)
|
||||
m_rql.append( QString(" opt:rep|%1").arg(rep) );
|
||||
}
|
||||
|
||||
setRql(m_rql);
|
||||
|
||||
return true;
|
||||
m_rep = rep;
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
void
|
||||
lastfm::RadioStation::setMainstr(float mainstr)
|
||||
{
|
||||
if ( m_rql.isEmpty() )
|
||||
return false;
|
||||
|
||||
int indexIn = rxMainstr.indexIn(m_rql);
|
||||
|
||||
if ( indexIn != -1 )
|
||||
{
|
||||
if (mainstr != k_defaultMainstr)
|
||||
m_rql.replace( indexIn, rxMainstr.capturedTexts()[0].length(), QString("opt:mainstr|%1").arg(mainstr) );
|
||||
else
|
||||
m_rql.replace( indexIn, rxMainstr.capturedTexts()[0].length(), "" );
|
||||
}
|
||||
else
|
||||
{
|
||||
// the rql doesn't have rep in it
|
||||
// so append it to the end
|
||||
if ( mainstr != k_defaultMainstr )
|
||||
m_rql.append( QString(" opt:mainstr|%1").arg(mainstr) );
|
||||
}
|
||||
|
||||
setRql(m_rql);
|
||||
|
||||
return true;
|
||||
m_mainstr = mainstr;
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
void
|
||||
lastfm::RadioStation::setDisco(bool disco)
|
||||
{
|
||||
if ( m_rql.isEmpty() )
|
||||
return false;
|
||||
|
||||
int indexIn = rxDisco.indexIn(m_rql);
|
||||
|
||||
if ( indexIn != -1 )
|
||||
{
|
||||
if (disco)
|
||||
m_rql.replace( indexIn, rxDisco.capturedTexts()[0].length(), "opt:discovery|true" );
|
||||
else
|
||||
m_rql.replace( indexIn, rxDisco.capturedTexts()[0].length(), "" );
|
||||
}
|
||||
else
|
||||
{
|
||||
// the rql doesn't have disco in it
|
||||
// so append it to the end if it is set
|
||||
|
||||
if (disco)
|
||||
m_rql.append( " opt:discovery|true" );
|
||||
}
|
||||
|
||||
setRql(m_rql);
|
||||
|
||||
return true;
|
||||
m_disco = disco;
|
||||
}
|
||||
|
||||
|
||||
float lastfm::RadioStation::rep() const
|
||||
{
|
||||
if ( rxRep.indexIn(m_rql) != -1 )
|
||||
return rxRep.capturedTexts()[1].toFloat();
|
||||
|
||||
return k_defaultRep;
|
||||
return m_rep;
|
||||
}
|
||||
|
||||
|
||||
float lastfm::RadioStation::mainstr() const
|
||||
{
|
||||
if ( rxMainstr.indexIn(m_rql) != -1 )
|
||||
return rxMainstr.capturedTexts()[1].toFloat();
|
||||
|
||||
return k_defaultMainstr;
|
||||
return m_mainstr;
|
||||
}
|
||||
|
||||
|
||||
bool lastfm::RadioStation::disco() const
|
||||
{
|
||||
if ( rxDisco.indexIn(m_rql) != -1 )
|
||||
return rxDisco.capturedTexts()[1] == "true";
|
||||
|
||||
return k_defaultDisco;
|
||||
return m_disco;
|
||||
}
|
||||
|
||||
|
||||
QString lastfm::RadioStation::libraryStr( QList<lastfm::User>& users )
|
||||
{
|
||||
qSort(users.begin(), users.end());
|
||||
|
||||
QString url = (users.count() > 1) ? "lastfm://users/" : "lastfm://user/";
|
||||
|
||||
url.append( users[0].name() );
|
||||
|
||||
for ( int i = 1 ; i < users.count() ; ++i )
|
||||
url.append( "," + users[i].name() );
|
||||
|
||||
url.append("/personal");
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
QString lastfm::RadioStation::tagStr( QList<lastfm::Tag>& tags )
|
||||
{
|
||||
qSort(tags.begin(), tags.end());
|
||||
|
||||
QString url = (tags.count() > 1) ? "lastfm://tag/" : "lastfm://globaltags/";
|
||||
|
||||
url.append( tags[0].name() );
|
||||
|
||||
for ( int i = 1 ; i < tags.count() ; ++i )
|
||||
url.append( "*" + tags[i].name() );
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
QString lastfm::RadioStation::similarStr( QList<lastfm::Artist>& artists )
|
||||
{
|
||||
qSort(artists.begin(), artists.end());
|
||||
|
||||
QString url = (artists.count() > 1) ? "lastfm://artistnames/" : "lastfm://artist/";
|
||||
|
||||
url.append( artists[0].name() );
|
||||
|
||||
for ( int i = 1 ; i < artists.count() ; ++i )
|
||||
url.append( "," + artists[i].name() );
|
||||
|
||||
if (artists.count() == 1)
|
||||
url.append( "/similarartists" );
|
||||
|
||||
return url;
|
||||
}
|
||||
|
86
thirdparty/liblastfm2/src/radio/RadioStation.h
vendored
86
thirdparty/liblastfm2/src/radio/RadioStation.h
vendored
@@ -38,38 +38,40 @@ namespace lastfm
|
||||
setString( s );
|
||||
}
|
||||
|
||||
static RadioStation library( const lastfm::User& user ) { return rql( libraryStr( user ) ); }
|
||||
static RadioStation recommendations( const lastfm::User& user ) { return rql( recommendationsStr( user ) ); }
|
||||
static RadioStation neighbourhood( const lastfm::User& user ) { return rql( neighbourhoodStr( user ) ); }
|
||||
static RadioStation lovedTracks( const lastfm::User& user ) { return rql( lovedTracksStr( user ) ); }
|
||||
static RadioStation globalTag( const lastfm::Tag& tag ) { return rql( globalTagStr( tag ) ); }
|
||||
static RadioStation similar( const lastfm::Artist& artist ) { return rql( similarStr( artist ) ); }
|
||||
static RadioStation userTag( const lastfm::User& user, const lastfm::Tag& tag) { return rql( userTagStr( user, tag ) ); }
|
||||
static RadioStation playlist( int playlistId ) { return rql( playlistStr( playlistId ) ); }
|
||||
static RadioStation adventure( const lastfm::User& user ) { return rql( adventureStr( user ) ); }
|
||||
static RadioStation library( const lastfm::User& user );
|
||||
static RadioStation library( QList<lastfm::User>& users );
|
||||
|
||||
static RadioStation rql( const QString& rql )
|
||||
{
|
||||
RadioStation station;
|
||||
station.setRql( rql );
|
||||
return station;
|
||||
}
|
||||
static RadioStation similar( const lastfm::Artist& artist );
|
||||
static RadioStation similar( QList<lastfm::Artist>& artist );
|
||||
|
||||
static RadioStation tag( const lastfm::Tag& tag );
|
||||
static RadioStation tag( QList<lastfm::Tag>& tag );
|
||||
|
||||
static RadioStation recommendations( const lastfm::User& user );
|
||||
static RadioStation friends( const lastfm::User& user );
|
||||
static RadioStation neighbourhood( const lastfm::User& user );
|
||||
|
||||
static RadioStation mix( const lastfm::User& user );
|
||||
|
||||
QNetworkReply* getSampleArtists( int limit = 50 ) const;
|
||||
QNetworkReply* getTagSuggestions( int limit = 50 ) const;
|
||||
|
||||
/** eg. "mxcl's Loved Tracks"
|
||||
* It is worth noting that the Radio doesn't set the title of RadioStation
|
||||
* object until we have tuned to it, and then we only set the one we give
|
||||
* you back.
|
||||
*/
|
||||
QString title() const { return m_title; }
|
||||
QString title() const;
|
||||
/** the Last.fm url, eg. lastfm://user/mxcl/loved */
|
||||
QString url() const { return m_url; }
|
||||
QString rql() const { return m_rql; }
|
||||
QString url() const;
|
||||
|
||||
void setTitle( const QString& s ) { m_title = s; }
|
||||
void setTitle( const QString& title );
|
||||
|
||||
bool setRep(float rep);
|
||||
bool setMainstr(float mainstr);
|
||||
bool setDisco(bool disco);
|
||||
void setTagFilter( const QString& tag );
|
||||
|
||||
void setRep(float rep);
|
||||
void setMainstr(float mainstr);
|
||||
void setDisco(bool disco);
|
||||
|
||||
float rep() const;
|
||||
float mainstr() const;
|
||||
@@ -77,37 +79,35 @@ namespace lastfm
|
||||
|
||||
bool isLegacyPlaylist() const
|
||||
{
|
||||
return m_url.startsWith( "lastfm://play/" ) ||
|
||||
m_url.startsWith( "lastfm://preview/" ) ||
|
||||
m_url.startsWith( "lastfm://track/" ) ||
|
||||
m_url.startsWith( "lastfm://playlist/" );
|
||||
return m_url.toString().startsWith( "lastfm://play/" ) ||
|
||||
m_url.toString().startsWith( "lastfm://preview/" ) ||
|
||||
m_url.toString().startsWith( "lastfm://track/" ) ||
|
||||
m_url.toString().startsWith( "lastfm://playlist/" );
|
||||
}
|
||||
|
||||
// good for getRecentStations:
|
||||
static QList<RadioStation> list( QNetworkReply* );
|
||||
|
||||
private:
|
||||
void setRql( const QString& rql )
|
||||
{
|
||||
m_rql = rql;
|
||||
m_url = "lastfm://rql/" + QString(rql.toUtf8().toBase64());
|
||||
}
|
||||
bool operator==( const RadioStation& that ) const;
|
||||
|
||||
private:
|
||||
void setString( const QString& s );
|
||||
|
||||
static QString libraryStr( const lastfm::User& user ) { return "library:" + user ; }
|
||||
static QString recommendationsStr( const lastfm::User& user ) { return "rec:" + user ; }
|
||||
static QString neighbourhoodStr( const lastfm::User& user ) { return "neigh:" + user ; }
|
||||
static QString lovedTracksStr( const lastfm::User& user ) { return "loved:" + user ; }
|
||||
static QString globalTagStr( const lastfm::Tag& tag ) { return "tag:\"" + tag + "\"" ; }
|
||||
static QString similarStr( const lastfm::Artist& artist ) { return "simart:\"" + artist + "\""; }
|
||||
static QString userTagStr( const lastfm::User& user, const lastfm::Tag& tag) { return "ptag:\"" + tag + "\"|" + user ; }
|
||||
static QString playlistStr( int playlistId ) { return "playlist:" + QString::number(playlistId) ; }
|
||||
static QString adventureStr( const lastfm::User& user ) { return "adv:" + user ; }
|
||||
static QString libraryStr( QList<lastfm::User>& user );
|
||||
static QString recommendationsStr( const lastfm::User& user ) { return "lastfm://user/" + user + "/recommended"; }
|
||||
static QString friendsStr( const lastfm::User& user ) { return "lastfm://user/" + user + "/friends"; }
|
||||
static QString neighbourhoodStr( const lastfm::User& user ) { return "lastfm://user/" + user + "/neighbours"; }
|
||||
static QString tagStr( QList<lastfm::Tag>& tag );
|
||||
static QString similarStr( QList<lastfm::Artist>& artist );
|
||||
static QString mixStr( const lastfm::User& user ) { return "lastfm://user/" + user + "/mix"; }
|
||||
private:
|
||||
QString m_rql;
|
||||
QString m_url;
|
||||
QUrl m_url;
|
||||
QString m_title;
|
||||
QString m_tagFilter;
|
||||
|
||||
float m_rep;
|
||||
float m_mainstr;
|
||||
bool m_disco;
|
||||
};
|
||||
}
|
||||
|
||||
|
161
thirdparty/liblastfm2/src/radio/RadioTuner.cpp
vendored
161
thirdparty/liblastfm2/src/radio/RadioTuner.cpp
vendored
@@ -17,10 +17,14 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with liblastfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include "RadioTuner.h"
|
||||
#include "../core/XmlQuery.h"
|
||||
#include "../types/Xspf.h"
|
||||
#include "../ws/ws.h"
|
||||
|
||||
using namespace lastfm;
|
||||
|
||||
//TODO skips left
|
||||
@@ -32,34 +36,37 @@ using namespace lastfm;
|
||||
|
||||
|
||||
RadioTuner::RadioTuner( const RadioStation& station )
|
||||
: m_retry_counter( 0 )
|
||||
: m_retry_counter( 0 ), m_fetchingPlaylist( false ), m_requestedPlaylist(false)
|
||||
{
|
||||
m_twoSecondTimer = new QTimer( this );
|
||||
m_twoSecondTimer->setSingleShot( true );
|
||||
connect( m_twoSecondTimer, SIGNAL(timeout()), SLOT(onTwoSecondTimeout()));
|
||||
|
||||
qDebug() << station.url();
|
||||
|
||||
//Empty RadioStation implies that the radio
|
||||
//should tune to the previous station.
|
||||
if( station.url().isEmpty() ) {
|
||||
if( station.url().isEmpty() )
|
||||
{
|
||||
fetchFiveMoreTracks();
|
||||
return;
|
||||
}
|
||||
|
||||
QMap<QString, QString> map;
|
||||
map["method"] = "radio.tune";
|
||||
map["station"] = station.url();
|
||||
map["additional_info"] = "1";
|
||||
QNetworkReply* reply = ws::post(map);
|
||||
connect( reply, SIGNAL(finished()), SLOT(onTuneReturn()) );
|
||||
else
|
||||
{
|
||||
QMap<QString, QString> map;
|
||||
map["method"] = "radio.tune";
|
||||
map["station"] = station.url();
|
||||
map["additional_info"] = "1";
|
||||
connect( ws::post(map), SIGNAL(finished()), SLOT(onTuneReturn()) );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RadioTuner::retune( const RadioStation& station)
|
||||
RadioTuner::retune( const RadioStation& station )
|
||||
{
|
||||
m_queue.clear();
|
||||
m_playlistQueue.clear();
|
||||
m_retuneStation = station;
|
||||
|
||||
QMap<QString, QString> map;
|
||||
map["method"] = "radio.tune";
|
||||
map["station"] = station.url();
|
||||
map["additional_info"] = "1";
|
||||
QNetworkReply* reply = ws::post(map);
|
||||
connect( reply, SIGNAL(finished()), SLOT(onTuneReturn()) );
|
||||
qDebug() << station.url();
|
||||
}
|
||||
|
||||
|
||||
@@ -67,10 +74,8 @@ void
|
||||
RadioTuner::onTuneReturn()
|
||||
{
|
||||
try {
|
||||
XmlQuery lfm = ws::parse( (QNetworkReply*)sender() );
|
||||
// TODO: uncomment this is we are to get a radio station
|
||||
// name when we tune to an rql radio station
|
||||
//emit title( lfm["station"]["name"].text() );
|
||||
XmlQuery lfm = qobject_cast<QNetworkReply*>(sender())->readAll();
|
||||
emit title( lfm["station"]["name"].text() );
|
||||
|
||||
qDebug() << lfm;
|
||||
|
||||
@@ -79,22 +84,43 @@ RadioTuner::onTuneReturn()
|
||||
}
|
||||
catch (ws::ParseError& e)
|
||||
{
|
||||
emit error( e.enumValue() );
|
||||
emit error( e.enumValue(), e.message() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
void
|
||||
RadioTuner::fetchFiveMoreTracks()
|
||||
{
|
||||
//TODO check documentation, I figure this needs a session key
|
||||
QMap<QString, QString> map;
|
||||
map["method"] = "radio.getPlaylist";
|
||||
map["additional_info"] = "1";
|
||||
map["rtp"] = "1"; // see above
|
||||
QNetworkReply* reply = ws::post( map );
|
||||
connect( reply, SIGNAL(finished()), SLOT(onGetPlaylistReturn()) );
|
||||
return true;
|
||||
if ( !m_retuneStation.url().isEmpty() )
|
||||
{
|
||||
// We have been asked to retune so do it now
|
||||
QMap<QString, QString> map;
|
||||
map["method"] = "radio.tune";
|
||||
map["station"] = m_retuneStation.url();
|
||||
map["additional_info"] = "1";
|
||||
|
||||
QNetworkReply* reply = ws::post(map);
|
||||
connect( reply, SIGNAL(finished()), SLOT(onTuneReturn()) );
|
||||
|
||||
m_retuneStation = RadioStation();
|
||||
m_twoSecondTimer->stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !m_twoSecondTimer->isActive() )
|
||||
{
|
||||
//TODO check documentation, I figure this needs a session key
|
||||
QMap<QString, QString> map;
|
||||
map["method"] = "radio.getPlaylist";
|
||||
map["additional_info"] = "1";
|
||||
map["rtp"] = "1"; // see above
|
||||
connect( ws::post( map ), SIGNAL(finished()), SLOT(onGetPlaylistReturn()) );
|
||||
m_fetchingPlaylist = true;
|
||||
}
|
||||
else
|
||||
m_requestedPlaylist = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,39 +139,80 @@ RadioTuner::tryAgain()
|
||||
void
|
||||
RadioTuner::onGetPlaylistReturn()
|
||||
{
|
||||
// We shouldn't request another playlist for 2 seconds because we'll get the same one
|
||||
// in a different order. This QTimer will block until it has finished. If one or more
|
||||
// playlists have been requested in the meantime, it will fetch one on timeout
|
||||
m_twoSecondTimer->start( 2000 );
|
||||
|
||||
// This will block us fetching two playlists at once
|
||||
m_fetchingPlaylist = false;
|
||||
|
||||
try {
|
||||
XmlQuery lfm = ws::parse( (QNetworkReply*)sender() );
|
||||
Xspf xspf( lfm["playlist"] );
|
||||
QList<Track> tracks( xspf.tracks() );
|
||||
if (tracks.isEmpty()) {
|
||||
XmlQuery lfm = qobject_cast<QNetworkReply*>(sender())->readAll();
|
||||
emit title( lfm["playlist"]["title"].text() );
|
||||
|
||||
qDebug() << lfm;
|
||||
|
||||
Xspf* xspf = new Xspf( lfm["playlist"], this );
|
||||
connect( xspf, SIGNAL(expired()), SLOT(onXspfExpired()) );
|
||||
|
||||
if ( xspf->isEmpty() )
|
||||
{
|
||||
// give up after too many empty playlists :(
|
||||
if (!tryAgain())
|
||||
emit error( ws::NotEnoughContent );
|
||||
} else {
|
||||
emit error( ws::NotEnoughContent, tr("Not enough content") );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_retry_counter = 0;
|
||||
foreach (Track t, tracks)
|
||||
MutableTrack( t ).setSource( Track::LastFmRadio );
|
||||
m_queue += tracks;
|
||||
m_playlistQueue << xspf;
|
||||
emit trackAvailable();
|
||||
}
|
||||
}
|
||||
catch (ws::ParseError& e)
|
||||
{
|
||||
qWarning() << e.what();
|
||||
emit error( e.enumValue() );
|
||||
emit error( e.enumValue(), e.message() );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RadioTuner::onTwoSecondTimeout()
|
||||
{
|
||||
if (m_requestedPlaylist)
|
||||
{
|
||||
m_requestedPlaylist = false;
|
||||
fetchFiveMoreTracks();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RadioTuner::onXspfExpired()
|
||||
{
|
||||
int index = m_playlistQueue.indexOf( static_cast<Xspf*>(sender()) );
|
||||
if ( index != -1 )
|
||||
m_playlistQueue.takeAt( index )->deleteLater();
|
||||
}
|
||||
|
||||
Track
|
||||
RadioTuner::takeNextTrack()
|
||||
{
|
||||
//TODO presumably, we should check if fetchMoreTracks is working?
|
||||
if (m_queue.isEmpty())
|
||||
if ( m_playlistQueue.isEmpty() )
|
||||
{
|
||||
// If there are no tracks here and we're not fetching tracks
|
||||
// it's probably because the playlist expired so fetch more now
|
||||
if ( !m_fetchingPlaylist )
|
||||
fetchFiveMoreTracks();
|
||||
|
||||
return Track();
|
||||
|
||||
Track result = m_queue.takeFirst();
|
||||
if (m_queue.isEmpty())
|
||||
}
|
||||
|
||||
Track result = m_playlistQueue[0]->takeFirst();
|
||||
|
||||
if ( m_playlistQueue[0]->isEmpty() )
|
||||
m_playlistQueue.removeFirst();
|
||||
|
||||
if ( m_playlistQueue.isEmpty() )
|
||||
fetchFiveMoreTracks();
|
||||
|
||||
return result;
|
||||
|
15
thirdparty/liblastfm2/src/radio/RadioTuner.h
vendored
15
thirdparty/liblastfm2/src/radio/RadioTuner.h
vendored
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <lastfm/RadioStation>
|
||||
#include <lastfm/Track>
|
||||
#include <lastfm/Xspf>
|
||||
#include <lastfm/ws.h>
|
||||
#include <QList>
|
||||
|
||||
@@ -47,11 +48,14 @@ namespace lastfm
|
||||
void title( const QString& );
|
||||
void supportsDisco( bool supportsDisco );
|
||||
void trackAvailable();
|
||||
void error( lastfm::ws::Error );
|
||||
void error( lastfm::ws::Error, const QString& message );
|
||||
|
||||
private slots:
|
||||
void onTuneReturn();
|
||||
void onGetPlaylistReturn();
|
||||
void onXspfExpired();
|
||||
|
||||
void onTwoSecondTimeout();
|
||||
|
||||
private:
|
||||
/** Tries again up to 5 times
|
||||
@@ -66,10 +70,15 @@ namespace lastfm
|
||||
* is also not allowed according to our terms and conditions, which you
|
||||
* already agreed to in order to get your API key. Sorry about that dude.
|
||||
*/
|
||||
bool fetchFiveMoreTracks();
|
||||
void fetchFiveMoreTracks();
|
||||
|
||||
QList<Track> m_queue;
|
||||
private:
|
||||
QList<Xspf*> m_playlistQueue;
|
||||
uint m_retry_counter;
|
||||
bool m_fetchingPlaylist;
|
||||
bool m_requestedPlaylist;
|
||||
class QTimer* m_twoSecondTimer;
|
||||
RadioStation m_retuneStation;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -88,7 +88,7 @@ lastfm::Audioscrobbler::cache( const Track& track )
|
||||
|
||||
|
||||
void
|
||||
lastfm::Audioscrobbler::cacheBatch( const QList<Track>& tracks )
|
||||
lastfm::Audioscrobbler::cacheBatch( const QList<lastfm::Track>& tracks )
|
||||
{
|
||||
d->m_cache.add( tracks );
|
||||
|
||||
@@ -151,13 +151,28 @@ lastfm::Audioscrobbler::parseTrack( const XmlQuery& trackXml, const Track& track
|
||||
void
|
||||
lastfm::Audioscrobbler::onNowPlayingReturn()
|
||||
{
|
||||
lastfm::XmlQuery lfm = static_cast<QNetworkReply*>(sender())->readAll();
|
||||
qDebug() << lfm;
|
||||
try
|
||||
{
|
||||
lastfm::XmlQuery lfm = static_cast<QNetworkReply*>(sender())->readAll();
|
||||
|
||||
if ( lfm.attribute("status") == "ok" )
|
||||
parseTrack( lfm["nowplaying"], d->m_nowPlayingTrack );
|
||||
else
|
||||
emit nowPlayingError( lfm["error"].attribute("code").toInt(), lfm["error"].text() );
|
||||
qDebug() << lfm;
|
||||
|
||||
if ( lfm.attribute("status") == "ok" )
|
||||
parseTrack( lfm["nowplaying"], d->m_nowPlayingTrack );
|
||||
else
|
||||
emit nowPlayingError( lfm["error"].attribute("code").toInt(), lfm["error"].text() );
|
||||
|
||||
d->m_nowPlayingTrack = Track();
|
||||
d->m_nowPlayingReply = 0;
|
||||
}
|
||||
catch ( lastfm::ws::ParseError p )
|
||||
{
|
||||
qDebug() << p.message() << p.enumValue();
|
||||
}
|
||||
catch ( lastfm::ws::Error p )
|
||||
{
|
||||
qDebug() << p;
|
||||
}
|
||||
|
||||
d->m_nowPlayingTrack = Track();
|
||||
d->m_nowPlayingReply = 0;
|
||||
@@ -167,37 +182,65 @@ lastfm::Audioscrobbler::onNowPlayingReturn()
|
||||
void
|
||||
lastfm::Audioscrobbler::onTrackScrobbleReturn()
|
||||
{
|
||||
lastfm::XmlQuery lfm = d->m_scrobbleReply->readAll();
|
||||
qDebug() << lfm;
|
||||
|
||||
if (lfm.attribute("status") == "ok")
|
||||
try
|
||||
{
|
||||
int index = 0;
|
||||
lastfm::XmlQuery lfm = d->m_scrobbleReply->readAll();
|
||||
|
||||
foreach ( const XmlQuery& scrobble, lfm["scrobbles"].children("scrobble") )
|
||||
parseTrack( scrobble, d->m_batch.at( index++ ) );
|
||||
qDebug() << lfm;
|
||||
|
||||
d->m_cache.remove( d->m_batch );
|
||||
d->m_batch.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The scrobble submission failed
|
||||
|
||||
if ( !(lfm["error"].attribute("code") == "9" // Bad session
|
||||
|| lfm["error"].attribute("code") == "11" // Service offline
|
||||
|| lfm["error"].attribute("code") == "16") ) // Service temporarily unavailable
|
||||
if (lfm.attribute("status") == "ok")
|
||||
{
|
||||
// clear the cache if it was not one of these error codes
|
||||
int index = 0;
|
||||
|
||||
foreach ( const XmlQuery& scrobble, lfm["scrobbles"].children("scrobble") )
|
||||
parseTrack( scrobble, d->m_batch.at( index++ ) );
|
||||
|
||||
emit scrobblesSubmitted( d->m_batch );
|
||||
|
||||
d->m_cache.remove( d->m_batch );
|
||||
d->m_batch.clear();
|
||||
}
|
||||
else
|
||||
else if ( d->m_scrobbleReply->error() == QNetworkReply::NoError )
|
||||
{
|
||||
qWarning() << "Got error in scrobble submission:" << lfm[ "error" ] << "and silently ignoring. Submission is cached.";
|
||||
//Q_ASSERT(false);
|
||||
}
|
||||
}
|
||||
// The scrobble submission failed, but the http request was sucessful
|
||||
|
||||
d->m_scrobbleReply = 0;
|
||||
if ( !(lfm["error"].attribute("code") == "9" // Bad session
|
||||
|| lfm["error"].attribute("code") == "11" // Service offline
|
||||
|| lfm["error"].attribute("code") == "16") ) // Service temporarily unavailable
|
||||
{
|
||||
foreach ( const Track& track, d->m_batch )
|
||||
{
|
||||
MutableTrack mTrack = MutableTrack( track );
|
||||
mTrack.setScrobbleError( static_cast<Track::ScrobbleError>(lfm["error"].attribute("code").toInt()) );
|
||||
mTrack.setScrobbleErrorText( lfm["error"].text() );
|
||||
mTrack.setScrobbleStatus( Track::Error );
|
||||
}
|
||||
|
||||
emit scrobblesSubmitted( d->m_batch );
|
||||
|
||||
// clear the cache if it was not one of these error codes
|
||||
d->m_cache.remove( d->m_batch );
|
||||
d->m_batch.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
d->m_scrobbleReply = 0;
|
||||
|
||||
// check is there are anymore scrobbles to submit
|
||||
submit();
|
||||
}
|
||||
catch ( lastfm::ws::ParseError p )
|
||||
{
|
||||
qDebug() << p.message() << p.enumValue();
|
||||
d->m_scrobbleReply = 0;
|
||||
}
|
||||
catch ( lastfm::ws::Error p )
|
||||
{
|
||||
qDebug() << p;
|
||||
d->m_scrobbleReply = 0;
|
||||
}
|
||||
}
|
||||
|
@@ -47,6 +47,9 @@ namespace lastfm
|
||||
|
||||
signals:
|
||||
void scrobblesCached( const QList<lastfm::Track>& tracks );
|
||||
/* Note that this is emitted after we tried to submit the scrobbles
|
||||
It could just be that they have an error code */
|
||||
void scrobblesSubmitted( const QList<lastfm::Track>& tracks );
|
||||
void nowPlayingError( int code, QString message );
|
||||
|
||||
public slots:
|
||||
@@ -55,7 +58,7 @@ namespace lastfm
|
||||
void nowPlaying( const Track& );
|
||||
/** will cache the track and call submit() */
|
||||
void cache( const Track& );
|
||||
void cacheBatch( const QList<Track>& );
|
||||
void cacheBatch( const QList<lastfm::Track>& );
|
||||
|
||||
/** will submit the submission cache for this user */
|
||||
void submit();
|
||||
|
6
thirdparty/liblastfm2/src/types/Album.cpp
vendored
6
thirdparty/liblastfm2/src/types/Album.cpp
vendored
@@ -28,14 +28,14 @@
|
||||
#include <QTimer>
|
||||
|
||||
QNetworkReply*
|
||||
lastfm::Album::getInfo(const QString& user, const QString& sk) const
|
||||
lastfm::Album::getInfo() const
|
||||
{
|
||||
QMap<QString, QString> map;
|
||||
map["method"] = "album.getInfo";
|
||||
map["artist"] = m_artist;
|
||||
map["album"] = m_title;
|
||||
if (!user.isEmpty()) map["username"] = user;
|
||||
if (!sk.isEmpty()) map["sk"] = sk;
|
||||
if (!lastfm::ws::Username.isEmpty()) map["username"] = lastfm::ws::Username;
|
||||
if (!lastfm::ws::SessionKey.isEmpty()) map["sk"] = lastfm::ws::SessionKey;
|
||||
return lastfm::ws::get(map);
|
||||
}
|
||||
|
||||
|
2
thirdparty/liblastfm2/src/types/Album.h
vendored
2
thirdparty/liblastfm2/src/types/Album.h
vendored
@@ -55,7 +55,7 @@ namespace lastfm
|
||||
bool isNull() const { return m_title.isEmpty() && m_mbid.isNull(); }
|
||||
|
||||
/** Album.getInfo WebService */
|
||||
QNetworkReply* getInfo(const QString& user = "", const QString& sk = "") const;
|
||||
QNetworkReply* getInfo() const;
|
||||
QNetworkReply* share( const QStringList& recipients, const QString& message = "", bool isPublic = true ) const;
|
||||
|
||||
/** use Tag::list to get the tag list out of the finished reply */
|
||||
|
20
thirdparty/liblastfm2/src/types/Artist.cpp
vendored
20
thirdparty/liblastfm2/src/types/Artist.cpp
vendored
@@ -94,11 +94,11 @@ Artist::getEvents(int limit) const
|
||||
}
|
||||
|
||||
QNetworkReply*
|
||||
Artist::getInfo(const QString& user, const QString& sk) const
|
||||
Artist::getInfo() const
|
||||
{
|
||||
QMap<QString, QString> map = params("getInfo");
|
||||
if (!user.isEmpty()) map["username"] = user;
|
||||
if (!sk.isEmpty()) map["sk"] = sk;
|
||||
if (!lastfm::ws::Username.isEmpty()) map["username"] = lastfm::ws::Username;
|
||||
if (!lastfm::ws::SessionKey.isEmpty()) map["sk"] = lastfm::ws::SessionKey;
|
||||
return ws::get( map );
|
||||
}
|
||||
|
||||
@@ -124,9 +124,11 @@ Artist::getTopTracks() const
|
||||
|
||||
|
||||
QNetworkReply*
|
||||
Artist::getSimilar() const
|
||||
Artist::getSimilar( int limit ) const
|
||||
{
|
||||
return ws::get( params("getSimilar") );
|
||||
QMap<QString, QString> map = params("getSimilar");
|
||||
if ( limit != -1 ) map["limit"] = QString::number( limit );
|
||||
return ws::get( map );
|
||||
}
|
||||
|
||||
|
||||
@@ -145,7 +147,7 @@ Artist::getSimilar( QNetworkReply* r )
|
||||
QMap<int, QString> artists;
|
||||
try
|
||||
{
|
||||
XmlQuery lfm = ws::parse(r);
|
||||
XmlQuery lfm = r->readAll();
|
||||
foreach (XmlQuery e, lfm.children( "artist" ))
|
||||
{
|
||||
// convert floating percentage to int in range 0 to 10,000
|
||||
@@ -167,7 +169,7 @@ Artist::getTopTracks( QNetworkReply* r )
|
||||
QStringList tracks;
|
||||
try
|
||||
{
|
||||
XmlQuery lfm = ws::parse(r);
|
||||
XmlQuery lfm = r->readAll();
|
||||
foreach (XmlQuery e, lfm.children( "track" ))
|
||||
{
|
||||
tracks << e["name"].text();
|
||||
@@ -186,7 +188,7 @@ Artist::list( QNetworkReply* r )
|
||||
{
|
||||
QList<Artist> artists;
|
||||
try {
|
||||
XmlQuery lfm = ws::parse(r);
|
||||
XmlQuery lfm = r->readAll();
|
||||
foreach (XmlQuery xq, lfm.children( "artist" )) {
|
||||
Artist artist( xq );
|
||||
artists += artist;
|
||||
@@ -204,7 +206,7 @@ Artist
|
||||
Artist::getInfo( QNetworkReply* r )
|
||||
{
|
||||
try {
|
||||
XmlQuery lfm = ws::parse(r);
|
||||
XmlQuery lfm = r->readAll();
|
||||
Artist artist = lfm["artist"]["name"].text();
|
||||
artist.m_images = images( lfm["artist"] );
|
||||
return artist;
|
||||
|
38
thirdparty/liblastfm2/src/types/Artist.h
vendored
38
thirdparty/liblastfm2/src/types/Artist.h
vendored
@@ -49,14 +49,16 @@ namespace lastfm
|
||||
QUrl imageUrl( ImageSize size = Large, bool square = false ) const;
|
||||
|
||||
bool isNull() const { return m_name.isEmpty(); }
|
||||
|
||||
|
||||
/** the url for this artist's page at www.last.fm */
|
||||
QUrl www() const;
|
||||
|
||||
|
||||
Artist& operator=( const Artist& that ) { m_name = that.name(); m_images = that.m_images; return *this; }
|
||||
bool operator==( const Artist& that ) const { return m_name == that.m_name; }
|
||||
bool operator!=( const Artist& that ) const { return m_name != that.m_name; }
|
||||
|
||||
operator QString() const
|
||||
bool operator<( const Artist& that ) const { return m_name < that.m_name; }
|
||||
|
||||
operator QString() const
|
||||
{
|
||||
/** if no artist name is set, return the musicbrainz unknown identifier
|
||||
* in case some part of the GUI tries to display it anyway. Note isNull
|
||||
@@ -65,37 +67,37 @@ namespace lastfm
|
||||
}
|
||||
|
||||
QString toString() const { return name(); }
|
||||
QString name() const { return QString(*this); }
|
||||
QString name() const { return QString(*this); }
|
||||
|
||||
QDomElement toDomElement( QDomDocument& ) const { return QDomElement(); }
|
||||
|
||||
|
||||
QNetworkReply* share( const QStringList& recipients, const QString& message = "", bool isPublic = true ) const;
|
||||
|
||||
QNetworkReply* getEvents(int limit = 0) const;
|
||||
QNetworkReply* getInfo(const QString& user = "", const QString& sk = "") const;
|
||||
QNetworkReply* getInfo() const;
|
||||
static Artist getInfo( QNetworkReply* );
|
||||
|
||||
QNetworkReply* getSimilar() const;
|
||||
/** The match percentage is returned from last.fm as a 4 significant
|
||||
* figure floating point value. So we multply it by 100 to make an
|
||||
* integer in the range of 0 to 10,000. This is possible confusing
|
||||
* for you, but I felt it best not to lose any precision, and floats
|
||||
|
||||
QNetworkReply* getSimilar( int limit = -1 ) const;
|
||||
/** The match percentage is returned from last.fm as a 4 significant
|
||||
* figure floating point value. So we multply it by 100 to make an
|
||||
* integer in the range of 0 to 10,000. This is possible confusing
|
||||
* for you, but I felt it best not to lose any precision, and floats
|
||||
* aren't much fun. */
|
||||
static QMap<int, QString> getSimilar( QNetworkReply* );
|
||||
|
||||
|
||||
/** use Tag::list to get the tag list out of the finished reply */
|
||||
QNetworkReply* getTags() const;
|
||||
QNetworkReply* getTopTags() const;
|
||||
|
||||
|
||||
QNetworkReply* getTopTracks() const;
|
||||
static QStringList getTopTracks( QNetworkReply* );
|
||||
|
||||
|
||||
/** Last.fm dictates that you may submit at most 10 of these */
|
||||
QNetworkReply* addTags( const QStringList& ) const;
|
||||
|
||||
|
||||
QNetworkReply* search( int limit = -1 ) const;
|
||||
static QList<Artist> list( QNetworkReply* );
|
||||
|
||||
|
||||
QMap<QString, QString> params( const QString& method ) const;
|
||||
};
|
||||
}
|
||||
|
2
thirdparty/liblastfm2/src/types/Playlist.h
vendored
2
thirdparty/liblastfm2/src/types/Playlist.h
vendored
@@ -45,8 +45,6 @@ namespace lastfm
|
||||
|
||||
static QNetworkReply* create( const QString& title, const QString& description = "" );
|
||||
static QNetworkReply* fetch( const QUrl& url );
|
||||
|
||||
static Xspf fetch( QNetworkReply* );
|
||||
};
|
||||
}
|
||||
|
||||
|
2
thirdparty/liblastfm2/src/types/Tag.cpp
vendored
2
thirdparty/liblastfm2/src/types/Tag.cpp
vendored
@@ -63,7 +63,7 @@ Tag::list( QNetworkReply* r )
|
||||
{
|
||||
QMap<int, QString> tags;
|
||||
try {
|
||||
foreach (XmlQuery xq, XmlQuery(ws::parse(r)).children("tag"))
|
||||
foreach (XmlQuery xq, XmlQuery( r->readAll() ).children("tag"))
|
||||
// we toLower always as otherwise it is ugly mixed case, as first
|
||||
// ever tag decides case, and Last.fm is case insensitive about it
|
||||
// anyway
|
||||
|
3
thirdparty/liblastfm2/src/types/Tag.h
vendored
3
thirdparty/liblastfm2/src/types/Tag.h
vendored
@@ -37,6 +37,9 @@ namespace lastfm
|
||||
|
||||
operator QString() const { return m_name; }
|
||||
QString name() const { return m_name; }
|
||||
|
||||
lastfm::Tag operator=( const Tag& that ) const { return Tag( that.name() ); }
|
||||
bool operator<( const Tag& that ) const { return this->m_name < that.m_name; }
|
||||
|
||||
/** the global tag page at www.last.fm */
|
||||
QUrl www() const;
|
||||
|
20
thirdparty/liblastfm2/src/types/Tasteometer.cpp
vendored
Normal file
20
thirdparty/liblastfm2/src/types/Tasteometer.cpp
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "Tasteometer.h"
|
||||
|
||||
#include "lastfm.h"
|
||||
|
||||
lastfm::Tasteometer::Tasteometer()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply*
|
||||
lastfm::Tasteometer::compare( const User& left, const User& right )
|
||||
{
|
||||
QMap<QString, QString> map;
|
||||
map["method"] = "Tasteometer.compare";
|
||||
map["type1"] = "user";
|
||||
map["value1"] = left.name();
|
||||
map["type2"] = "user";
|
||||
map["value2"] = right.name();
|
||||
return lastfm::ws::get( map );
|
||||
}
|
18
thirdparty/liblastfm2/src/types/Tasteometer.h
vendored
Normal file
18
thirdparty/liblastfm2/src/types/Tasteometer.h
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef TASTEOMETER_H
|
||||
#define TASTEOMETER_H
|
||||
|
||||
#include <lastfm/global.h>
|
||||
|
||||
namespace lastfm
|
||||
{
|
||||
class LASTFM_DLLEXPORT Tasteometer
|
||||
{
|
||||
public:
|
||||
Tasteometer();
|
||||
|
||||
public:
|
||||
static QNetworkReply* compare( const User& left, const User& right );
|
||||
};
|
||||
}
|
||||
|
||||
#endif // TASTEOMETER_H
|
207
thirdparty/liblastfm2/src/types/Track.cpp
vendored
207
thirdparty/liblastfm2/src/types/Track.cpp
vendored
@@ -17,13 +17,58 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with liblastfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QStringList>
|
||||
#include <QAbstractNetworkCache>
|
||||
|
||||
#include "Track.h"
|
||||
#include "User.h"
|
||||
#include "../core/UrlBuilder.h"
|
||||
#include "../core/XmlQuery.h"
|
||||
#include "../ws/ws.h"
|
||||
#include <QFileInfo>
|
||||
#include <QStringList>
|
||||
|
||||
|
||||
lastfm::TrackContext::TrackContext()
|
||||
:m_type( Unknown )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
lastfm::TrackContext::TrackContext( const QString& type, const QList<QString>& values )
|
||||
:m_type( getType( type ) ), m_values( values )
|
||||
{
|
||||
}
|
||||
|
||||
lastfm::TrackContext::Type
|
||||
lastfm::TrackContext::getType( const QString& typeString )
|
||||
{
|
||||
Type type = Unknown;
|
||||
|
||||
if ( typeString == "artist" )
|
||||
type = Artist;
|
||||
else if ( typeString == "user" )
|
||||
type = User;
|
||||
else if ( typeString == "neighbour" )
|
||||
type = Neighbour;
|
||||
else if ( typeString == "friend" )
|
||||
type = Friend;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
lastfm::TrackContext::Type
|
||||
lastfm::TrackContext::type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
|
||||
QList<QString>
|
||||
lastfm::TrackContext::values() const
|
||||
{
|
||||
return m_values;
|
||||
}
|
||||
|
||||
|
||||
lastfm::TrackData::TrackData()
|
||||
@@ -33,9 +78,9 @@ lastfm::TrackData::TrackData()
|
||||
rating( 0 ),
|
||||
fpid( -1 ),
|
||||
loved( false ),
|
||||
null( false ),
|
||||
scrobbleStatus( Track::Null ),
|
||||
scrobbleError( Track::None ),
|
||||
null( false )
|
||||
scrobbleError( Track::None )
|
||||
{}
|
||||
|
||||
lastfm::Track::Track()
|
||||
@@ -51,32 +96,11 @@ lastfm::Track::Track( const QDomElement& e )
|
||||
d = new TrackData;
|
||||
|
||||
if (e.isNull()) { d->null = true; return; }
|
||||
|
||||
//TODO: not sure of lastfm's xml changed, but <track> nodes have
|
||||
// <artist><name>Artist Name</name><mbid>..<url></artist>
|
||||
// as children isntead of <artist>Artist Name<artist>
|
||||
// we detect both here.
|
||||
QDomNode artistName = e.namedItem( "artist" ).namedItem( "name" );
|
||||
if( artistName.isNull() ) {
|
||||
d->artist = e.namedItem( "artist" ).toElement().text();
|
||||
} else {
|
||||
d->artist = artistName.toElement().text();
|
||||
}
|
||||
|
||||
|
||||
d->artist = e.namedItem( "artist" ).toElement().text();
|
||||
d->albumArtist = e.namedItem( "albumArtist" ).toElement().text();
|
||||
d->album = e.namedItem( "album" ).toElement().text();
|
||||
|
||||
|
||||
//TODO: not sure if lastfm xml's changed, or if chart.getTopTracks uses
|
||||
//a different format, but the title is stored at
|
||||
//<track><name>Title</name>...
|
||||
//we detect both here.
|
||||
QDomNode trackTitle = e.namedItem( "name" );
|
||||
if( trackTitle.isNull() )
|
||||
d->title = e.namedItem( "track" ).toElement().text();
|
||||
else
|
||||
d->title = trackTitle.toElement().text();
|
||||
|
||||
d->title = e.namedItem( "track" ).toElement().text();
|
||||
d->correctedArtist = e.namedItem( "correctedArtist" ).toElement().text();
|
||||
d->correctedAlbumArtist = e.namedItem( "correctedAlbumArtist" ).toElement().text();
|
||||
d->correctedAlbum = e.namedItem( "correctedAlbum" ).toElement().text();
|
||||
@@ -108,9 +132,16 @@ lastfm::Track::Track( const QDomElement& e )
|
||||
void
|
||||
lastfm::TrackData::onLoveFinished()
|
||||
{
|
||||
XmlQuery lfm = static_cast<QNetworkReply*>(sender())->readAll();
|
||||
if ( lfm.attribute( "status" ) == "ok")
|
||||
loved = true;
|
||||
try
|
||||
{
|
||||
XmlQuery lfm = static_cast<QNetworkReply*>(sender())->readAll();
|
||||
if ( lfm.attribute( "status" ) == "ok")
|
||||
loved = true;
|
||||
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
emit loveToggled( loved );
|
||||
}
|
||||
|
||||
@@ -118,35 +149,51 @@ lastfm::TrackData::onLoveFinished()
|
||||
void
|
||||
lastfm::TrackData::onUnloveFinished()
|
||||
{
|
||||
XmlQuery lfm = static_cast<QNetworkReply*>(sender())->readAll();
|
||||
if ( lfm.attribute( "status" ) == "ok")
|
||||
loved = false;
|
||||
try
|
||||
{
|
||||
XmlQuery lfm = static_cast<QNetworkReply*>(sender())->readAll();
|
||||
if ( lfm.attribute( "status" ) == "ok")
|
||||
loved = false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
emit loveToggled( loved );
|
||||
}
|
||||
|
||||
void
|
||||
lastfm::TrackData::onGotInfo()
|
||||
{
|
||||
lastfm::XmlQuery lfm( static_cast<QNetworkReply*>(sender())->readAll() );
|
||||
const QByteArray data = static_cast<QNetworkReply*>(sender())->readAll();
|
||||
|
||||
QString imageUrl = lfm["track"]["image size=small"].text();
|
||||
if ( !imageUrl.isEmpty() ) m_images[lastfm::Small] = imageUrl;
|
||||
imageUrl = lfm["track"]["image size=medium"].text();
|
||||
if ( !imageUrl.isEmpty() ) m_images[lastfm::Medium] = imageUrl;
|
||||
imageUrl = lfm["track"]["image size=large"].text();
|
||||
if ( !imageUrl.isEmpty() ) m_images[lastfm::Large] = imageUrl;
|
||||
imageUrl = lfm["track"]["image size=extralarge"].text();
|
||||
if ( !imageUrl.isEmpty() ) m_images[lastfm::ExtraLarge] = imageUrl;
|
||||
imageUrl = lfm["track"]["image size=mega"].text();
|
||||
if ( !imageUrl.isEmpty() ) m_images[lastfm::Mega] = imageUrl;
|
||||
try
|
||||
{
|
||||
lastfm::XmlQuery lfm( data );
|
||||
|
||||
loved = lfm["track"]["userloved"].text().toInt();
|
||||
QString imageUrl = lfm["track"]["image size=small"].text();
|
||||
if ( !imageUrl.isEmpty() ) m_images[lastfm::Small] = imageUrl;
|
||||
imageUrl = lfm["track"]["image size=medium"].text();
|
||||
if ( !imageUrl.isEmpty() ) m_images[lastfm::Medium] = imageUrl;
|
||||
imageUrl = lfm["track"]["image size=large"].text();
|
||||
if ( !imageUrl.isEmpty() ) m_images[lastfm::Large] = imageUrl;
|
||||
imageUrl = lfm["track"]["image size=extralarge"].text();
|
||||
if ( !imageUrl.isEmpty() ) m_images[lastfm::ExtraLarge] = imageUrl;
|
||||
imageUrl = lfm["track"]["image size=mega"].text();
|
||||
if ( !imageUrl.isEmpty() ) m_images[lastfm::Mega] = imageUrl;
|
||||
|
||||
emit gotInfo( lfm );
|
||||
emit loveToggled( loved );
|
||||
loved = lfm["track"]["userloved"].text().toInt();
|
||||
|
||||
emit gotInfo( data );
|
||||
emit loveToggled( loved );
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
emit gotInfo( data );
|
||||
}
|
||||
|
||||
// you should connect everytime you call getInfo
|
||||
disconnect( this, SIGNAL(gotInfo(const XmlQuery&)), 0, 0);
|
||||
disconnect( this, SIGNAL(gotInfo(const QByteArray&)), 0, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -310,6 +357,21 @@ lastfm::Track::share( const QStringList& recipients, const QString& message, boo
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lastfm::Track::invalidateGetInfo()
|
||||
{
|
||||
// invalidate the track.getInfo cache
|
||||
QAbstractNetworkCache* cache = lastfm::nam()->cache();
|
||||
if ( cache )
|
||||
{
|
||||
QMap<QString, QString> map = params("getInfo", true);
|
||||
if (!lastfm::ws::Username.isEmpty()) map["username"] = lastfm::ws::Username;
|
||||
if (!lastfm::ws::SessionKey.isEmpty()) map["sk"] = lastfm::ws::SessionKey;
|
||||
cache->remove( lastfm::ws::url( map ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lastfm::MutableTrack::setFromLfm( const XmlQuery& lfm )
|
||||
{
|
||||
@@ -329,12 +391,20 @@ lastfm::MutableTrack::setFromLfm( const XmlQuery& lfm )
|
||||
d->forceLoveToggled( d->loved );
|
||||
}
|
||||
|
||||
void
|
||||
lastfm::MutableTrack::setImageUrl( lastfm::ImageSize size, const QString& url )
|
||||
{
|
||||
d->m_images[size] = url;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lastfm::MutableTrack::love()
|
||||
{
|
||||
QNetworkReply* reply = ws::post(params("love"));
|
||||
QObject::connect( reply, SIGNAL(finished()), signalProxy(), SLOT(onLoveFinished()));
|
||||
|
||||
invalidateGetInfo();
|
||||
}
|
||||
|
||||
|
||||
@@ -343,8 +413,9 @@ lastfm::MutableTrack::unlove()
|
||||
{
|
||||
QNetworkReply* reply = ws::post(params("unlove"));
|
||||
QObject::connect( reply, SIGNAL(finished()), signalProxy(), SLOT(onUnloveFinished()));
|
||||
}
|
||||
|
||||
invalidateGetInfo();
|
||||
}
|
||||
|
||||
QNetworkReply*
|
||||
lastfm::MutableTrack::ban()
|
||||
@@ -390,15 +461,24 @@ lastfm::Track::getTags() const
|
||||
}
|
||||
|
||||
void
|
||||
lastfm::Track::getInfo(const QString& user, const QString& sk) const
|
||||
lastfm::Track::getInfo() const
|
||||
{
|
||||
QMap<QString, QString> map = params("getInfo", true);
|
||||
if (!user.isEmpty()) map["username"] = user;
|
||||
if (!sk.isEmpty()) map["sk"] = sk;
|
||||
if (!lastfm::ws::Username.isEmpty()) map["username"] = lastfm::ws::Username;
|
||||
if (!lastfm::ws::SessionKey.isEmpty()) map["sk"] = lastfm::ws::SessionKey;
|
||||
QObject::connect( ws::get( map ), SIGNAL(finished()), d.data(), SLOT(onGotInfo()));
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply*
|
||||
lastfm::Track::getBuyLinks( const QString& country ) const
|
||||
{
|
||||
QMap<QString, QString> map = params( "getBuyLinks", true );
|
||||
map["country"] = country;
|
||||
return ws::get( map );
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply*
|
||||
lastfm::Track::addTags( const QStringList& tags ) const
|
||||
{
|
||||
@@ -423,9 +503,15 @@ lastfm::Track::removeTag( const QString& tag ) const
|
||||
|
||||
QNetworkReply*
|
||||
lastfm::Track::updateNowPlaying() const
|
||||
{
|
||||
return updateNowPlaying(duration());
|
||||
}
|
||||
|
||||
QNetworkReply*
|
||||
lastfm::Track::updateNowPlaying( int duration ) const
|
||||
{
|
||||
QMap<QString, QString> map = params("updateNowPlaying");
|
||||
map["duration"] = QString::number( duration() );
|
||||
map["duration"] = QString::number( duration );
|
||||
if ( !album().isNull() ) map["album"] = album();
|
||||
map["context"] = extra("playerId");
|
||||
|
||||
@@ -434,6 +520,17 @@ lastfm::Track::updateNowPlaying() const
|
||||
return ws::post(map);
|
||||
}
|
||||
|
||||
QNetworkReply*
|
||||
lastfm::Track::removeNowPlaying() const
|
||||
{
|
||||
QMap<QString, QString> map;
|
||||
map["method"] = "track.removeNowPlaying";
|
||||
|
||||
qDebug() << map;
|
||||
|
||||
return ws::post(map);
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply*
|
||||
lastfm::Track::scrobble() const
|
||||
@@ -477,7 +574,7 @@ lastfm::Track::scrobble(const QList<lastfm::Track>& tracks)
|
||||
QUrl
|
||||
lastfm::Track::www() const
|
||||
{
|
||||
return UrlBuilder( "music" ).slash( d->artist ).slash( album().isNull() ? QString("_") : album()).slash( d->title ).url();
|
||||
return UrlBuilder( "music" ).slash( artist( Corrected ) ).slash( album( Corrected ).isNull() ? QString("_") : album( Corrected )).slash( title( Corrected ) ).url();
|
||||
}
|
||||
|
||||
|
||||
|
44
thirdparty/liblastfm2/src/types/Track.h
vendored
44
thirdparty/liblastfm2/src/types/Track.h
vendored
@@ -33,6 +33,31 @@
|
||||
|
||||
namespace lastfm {
|
||||
|
||||
class LASTFM_DLLEXPORT TrackContext
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
Unknown,
|
||||
User,
|
||||
Friend,
|
||||
Neighbour,
|
||||
Artist
|
||||
};
|
||||
|
||||
TrackContext();
|
||||
TrackContext( const QString& type, const QList<QString>& values );
|
||||
|
||||
Type type() const;
|
||||
QList<QString> values() const;
|
||||
private:
|
||||
static Type getType( const QString& type );
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
QList<QString> m_values;
|
||||
};
|
||||
|
||||
class TrackData : public QObject, public QSharedData
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -51,6 +76,7 @@ public:
|
||||
QString correctedAlbumArtist;
|
||||
QString correctedAlbum;
|
||||
QString correctedTitle;
|
||||
TrackContext context;
|
||||
uint trackNumber;
|
||||
uint duration;
|
||||
short source;
|
||||
@@ -63,6 +89,7 @@ public:
|
||||
QMap<lastfm::ImageSize, QUrl> m_images;
|
||||
short scrobbleStatus;
|
||||
short scrobbleError;
|
||||
QString scrobbleErrorText;
|
||||
|
||||
//FIXME I hate this, but is used for radio trackauth etc.
|
||||
QMap<QString,QString> extras;
|
||||
@@ -83,13 +110,12 @@ signals:
|
||||
void loveToggled( bool love );
|
||||
void loveFinished();
|
||||
void unlovedFinished();
|
||||
void gotInfo( const XmlQuery& );
|
||||
void gotInfo( const QByteArray& );
|
||||
void scrobbleStatusChanged();
|
||||
void corrected( QString correction );
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Our track type. It's quite good, you may want to use it as your track type
|
||||
* in general. It is explicitly shared. Which means when you make a copy, they
|
||||
* both point to the same data still. This is like Qt's implicitly shared
|
||||
@@ -191,6 +217,7 @@ public:
|
||||
|
||||
ScrobbleStatus scrobbleStatus() const { return static_cast<ScrobbleStatus>(d->scrobbleStatus); }
|
||||
ScrobbleError scrobbleError() const { return static_cast<ScrobbleError>(d->scrobbleError); }
|
||||
QString scrobbleErrorText() const { return d->scrobbleErrorText; }
|
||||
|
||||
/** default separator is an en-dash */
|
||||
QString toString() const { return toString( Corrected ); }
|
||||
@@ -198,6 +225,8 @@ public:
|
||||
QString toString( const QChar& separator, Corrections corrections = Original ) const;
|
||||
/** the standard representation of this object as an XML node */
|
||||
QDomElement toDomElement( class QDomDocument& ) const;
|
||||
|
||||
TrackContext context() const { return d->context; }
|
||||
|
||||
QString extra( const QString& key ) const{ return d->extras[ key ]; }
|
||||
|
||||
@@ -219,7 +248,8 @@ public:
|
||||
QNetworkReply* getTags() const; // for the logged in user
|
||||
QNetworkReply* getTopTags() const;
|
||||
QNetworkReply* getTopFans() const;
|
||||
void getInfo(const QString& user = "", const QString& sk = "") const;
|
||||
void getInfo() const;
|
||||
QNetworkReply* getBuyLinks( const QString& country ) const;
|
||||
|
||||
/** you can only add 10 tags, we submit everything you give us, but the
|
||||
* docs state 10 only. Will return 0 if the list is empty. */
|
||||
@@ -229,6 +259,8 @@ public:
|
||||
|
||||
/** scrobble the track */
|
||||
QNetworkReply* updateNowPlaying() const;
|
||||
QNetworkReply* updateNowPlaying( int duration ) const;
|
||||
QNetworkReply* removeNowPlaying() const;
|
||||
QNetworkReply* scrobble() const;
|
||||
static QNetworkReply* scrobble(const QList<lastfm::Track>& tracks);
|
||||
|
||||
@@ -238,7 +270,7 @@ public:
|
||||
protected:
|
||||
QExplicitlySharedDataPointer<TrackData> d;
|
||||
QMap<QString, QString> params( const QString& method, bool use_mbid = false ) const;
|
||||
|
||||
void invalidateGetInfo();
|
||||
private:
|
||||
Track( TrackData* that_d ) : d( that_d )
|
||||
{}
|
||||
@@ -272,6 +304,7 @@ public:
|
||||
}
|
||||
|
||||
void setFromLfm( const XmlQuery& lfm );
|
||||
void setImageUrl( lastfm::ImageSize size, const QString& url );
|
||||
|
||||
void setArtist( QString artist ) { d->artist = artist.trimmed(); }
|
||||
void setAlbumArtist( QString albumArtist ) { d->albumArtist = albumArtist.trimmed(); }
|
||||
@@ -293,6 +326,7 @@ public:
|
||||
d->forceScrobbleStatusChanged();
|
||||
}
|
||||
void setScrobbleError( ScrobbleError scrobbleError ) { d->scrobbleError = scrobbleError; }
|
||||
void setScrobbleErrorText( const QString& scrobbleErrorText ) { d->scrobbleErrorText = scrobbleErrorText; }
|
||||
|
||||
/** you also must scrobble this track for the love to become permenant */
|
||||
void love();
|
||||
@@ -304,6 +338,8 @@ public:
|
||||
void setExtra( const QString& key, const QString& value ) { d->extras[key] = value; }
|
||||
void removeExtra( QString key ) { d->extras.remove( key ); }
|
||||
void setTimeStamp( const QDateTime& dt ) { d->time = dt; }
|
||||
|
||||
void setContext( TrackContext context ) { d->context = context;}
|
||||
};
|
||||
|
||||
|
||||
|
95
thirdparty/liblastfm2/src/types/User.cpp
vendored
95
thirdparty/liblastfm2/src/types/User.cpp
vendored
@@ -23,6 +23,7 @@
|
||||
#include "../core/XmlQuery.h"
|
||||
#include <QStringList>
|
||||
#include <lastfm/UserList>
|
||||
#include <QAbstractNetworkCache>
|
||||
|
||||
using lastfm::User;
|
||||
using lastfm::UserList;
|
||||
@@ -63,11 +64,22 @@ User::params(const QString& method) const
|
||||
|
||||
|
||||
QNetworkReply*
|
||||
User::getFriends( int perPage, int page ) const
|
||||
User::getFriends( bool recentTracks, int limit, int page ) const
|
||||
{
|
||||
QMap<QString, QString> map = params( "getFriends" );
|
||||
map["limit"] = QString::number(perPage);
|
||||
map["page"] = QString::number(page);
|
||||
map["limit"] = QString::number( limit );
|
||||
map["page"] = QString::number( page );
|
||||
if ( recentTracks ) map["recenttracks"] = "1";
|
||||
return ws::get( map );
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply*
|
||||
User::getFriendsListeningNow( int limit, int page ) const
|
||||
{
|
||||
QMap<QString, QString> map = params( "getFriendsListeningNow" );
|
||||
map["limit"] = QString::number( limit );
|
||||
map["page"] = QString::number( page );
|
||||
return ws::get( map );
|
||||
}
|
||||
|
||||
@@ -80,9 +92,13 @@ User::getTopTags() const
|
||||
|
||||
|
||||
QNetworkReply*
|
||||
User::getTopArtists() const
|
||||
User::getTopArtists( QString period, int limit, int page ) const
|
||||
{
|
||||
return ws::get( params( "getTopArtists" ) );
|
||||
QMap<QString, QString> map = params( "getTopArtists" );
|
||||
map["period"] = period;
|
||||
map["limit"] = QString::number( limit );
|
||||
map["page"] = QString::number( page );
|
||||
return ws::get( map );
|
||||
}
|
||||
|
||||
|
||||
@@ -94,21 +110,46 @@ User::getRecentArtists() const
|
||||
|
||||
|
||||
QNetworkReply*
|
||||
User::getRecentTracks() const
|
||||
User::getRecentTracks( int limit , int page ) const
|
||||
{
|
||||
return ws::get( params( "getRecentTracks" ) );
|
||||
}
|
||||
QMap<QString, QString> map = params( "getRecentTracks" );
|
||||
map["limit"] = QString::number( limit );
|
||||
map["page"] = QString::number( page );
|
||||
|
||||
QNetworkReply*
|
||||
User::getRecentStations() const
|
||||
{
|
||||
return ws::post( params( "getRecentStations" ) );
|
||||
QAbstractNetworkCache* cache = lastfm::nam()->cache();
|
||||
if ( cache )
|
||||
cache->remove( lastfm::ws::url( map ) );
|
||||
|
||||
return ws::get( map );
|
||||
}
|
||||
|
||||
QNetworkReply*
|
||||
User::getNeighbours() const
|
||||
User::getRecentStations( int limit, int page ) const
|
||||
{
|
||||
return ws::get( params( "getNeighbours" ) );
|
||||
QMap<QString, QString> map = params( "getRecentStations" );
|
||||
map["limit"] = QString::number( limit );
|
||||
map["page"] = QString::number( page );
|
||||
return ws::get( map );
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply*
|
||||
User::getRecommendedArtists( int limit, int page ) const
|
||||
{
|
||||
QMap<QString, QString> map = params( "getRecommendedArtists" );
|
||||
map["limit"] = QString::number( limit );
|
||||
map["page"] = QString::number( page );
|
||||
return ws::get( map );
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply*
|
||||
User::getNeighbours( int limit, int page ) const
|
||||
{
|
||||
QMap<QString, QString> map = params( "getNeighbours" );
|
||||
map["limit"] = QString::number( limit );
|
||||
map["page"] = QString::number( page );
|
||||
return ws::get( map );
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +165,7 @@ User::list( QNetworkReply* r )
|
||||
{
|
||||
UserList users;
|
||||
try {
|
||||
XmlQuery lfm = ws::parse(r);
|
||||
XmlQuery lfm = r->readAll();
|
||||
foreach (XmlQuery e, lfm.children( "user" ))
|
||||
{
|
||||
User u( e );
|
||||
@@ -185,7 +226,7 @@ UserDetails::UserDetails( QNetworkReply* reply )
|
||||
{
|
||||
try
|
||||
{
|
||||
XmlQuery user = XmlQuery(ws::parse(reply))["user"];
|
||||
XmlQuery user = XmlQuery( reply->readAll() )["user"];
|
||||
m_age = user["age"].text().toUInt();
|
||||
m_scrobbles = user["playcount"].text().toUInt();
|
||||
m_registered = QDateTime::fromTime_t(user["registered"].attribute("unixtime").toUInt());
|
||||
@@ -209,25 +250,15 @@ UserDetails::UserDetails( QNetworkReply* reply )
|
||||
QString
|
||||
UserDetails::getInfoString() const
|
||||
{
|
||||
#define tr QObject::tr
|
||||
;
|
||||
|
||||
QString text;
|
||||
if (m_gender.known() && m_age > 0 && m_scrobbles > 0)
|
||||
{
|
||||
text = tr("A %1, %2 years of age with %L3 scrobbles")
|
||||
.arg( m_gender.toString() )
|
||||
.arg( m_age )
|
||||
.arg( m_scrobbles );
|
||||
}
|
||||
else if (m_scrobbles > 0)
|
||||
{
|
||||
text = tr("%L1 scrobbles").arg( m_scrobbles );
|
||||
}
|
||||
|
||||
text = QObject::tr("%1").arg( m_realName.isEmpty() ? m_name : m_realName );
|
||||
if ( m_age ) text.append( QObject::tr(", %1").arg( m_age ) );
|
||||
if ( m_gender.known() ) text.append( QObject::tr(", %1").arg( m_gender.toString() ) );
|
||||
if ( !m_country.isEmpty() ) text.append( QObject::tr(", %1").arg( m_country ) );
|
||||
if ( m_scrobbles ) text.append( QObject::tr(", %L1 scrobbles").arg( m_scrobbles ) );
|
||||
|
||||
return text;
|
||||
|
||||
#undef tr
|
||||
}
|
||||
|
||||
void
|
||||
|
79
thirdparty/liblastfm2/src/types/User.h
vendored
79
thirdparty/liblastfm2/src/types/User.h
vendored
@@ -42,7 +42,9 @@ namespace lastfm
|
||||
|
||||
User( const class XmlQuery& xml );
|
||||
|
||||
lastfm::User& operator=( const lastfm::User& that ) { m_name = that.name(); m_images = that.m_images; m_realName = that.m_realName; m_match = that.m_match; return *this; }
|
||||
bool operator==(const lastfm::User& that) const { return m_name == that.m_name; }
|
||||
bool operator<(const lastfm::User& that) const { return m_name < that.m_name; }
|
||||
|
||||
operator QString() const { return m_name; }
|
||||
QString name() const { return m_name; }
|
||||
@@ -52,14 +54,16 @@ namespace lastfm
|
||||
QNetworkReply* getTopTags() const;
|
||||
|
||||
/** use User::list() on the response to get a QList<User> */
|
||||
QNetworkReply* getFriends(int perPage = 50, int page = 1) const;
|
||||
QNetworkReply* getNeighbours() const;
|
||||
QNetworkReply* getFriends( bool recentTracks = false, int limit = 50, int page = 1 ) const;
|
||||
QNetworkReply* getFriendsListeningNow( int limit = 50, int page = 1 ) const;
|
||||
QNetworkReply* getNeighbours( int limit = 50, int page = 1 ) const;
|
||||
|
||||
QNetworkReply* getPlaylists() const;
|
||||
QNetworkReply* getTopArtists() const;
|
||||
QNetworkReply* getRecentTracks() const;
|
||||
QNetworkReply* getTopArtists( QString period = "overall", int limit = 50, int page = 1 ) const;
|
||||
QNetworkReply* getRecentTracks( int limit = 50, int page = 1 ) const;
|
||||
QNetworkReply* getRecentArtists() const;
|
||||
QNetworkReply* getRecentStations() const;
|
||||
QNetworkReply* getRecentStations( int limit = 10, int page = 1 ) const;
|
||||
QNetworkReply* getRecommendedArtists( int limit = 50, int page = 1 ) const;
|
||||
|
||||
static UserList list( QNetworkReply* );
|
||||
|
||||
@@ -90,6 +94,35 @@ namespace lastfm
|
||||
QMap<QString, QString> params( const QString& method ) const;
|
||||
};
|
||||
|
||||
class LASTFM_DLLEXPORT Gender
|
||||
{
|
||||
QString s;
|
||||
|
||||
public:
|
||||
Gender() :s(/*confused!*/){}
|
||||
|
||||
Gender( const QString& ss ) :s( ss.toLower() )
|
||||
{}
|
||||
|
||||
bool known() const { return male() || female(); }
|
||||
bool male() const { return s == "m"; }
|
||||
bool female() const { return s == "f"; }
|
||||
|
||||
QString toString() const
|
||||
{
|
||||
QString result;
|
||||
|
||||
if (male())
|
||||
result = QObject::tr( "m" );
|
||||
else if (female())
|
||||
result = QObject::tr( "f" );
|
||||
else
|
||||
result = QObject::tr( "n" ); // as in neuter
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** The Extended User contains extra information about a user's account */
|
||||
class LASTFM_DLLEXPORT UserDetails : public User
|
||||
@@ -109,6 +142,8 @@ namespace lastfm
|
||||
bool canBootstrap() const{ return m_canBootstrap; }
|
||||
quint32 scrobbleCount() const{ return m_scrobbles; }
|
||||
QDateTime dateRegistered() const { return m_registered; }
|
||||
Gender gender() const { return m_gender; }
|
||||
QString country() const { return m_country; }
|
||||
|
||||
void setScrobbleCount( quint32 scrobblesCount );
|
||||
void setDateRegistered( const QDateTime& date );
|
||||
@@ -127,39 +162,7 @@ namespace lastfm
|
||||
// static QNetworkReply* getRecommendedArtists();
|
||||
|
||||
protected:
|
||||
|
||||
class Gender
|
||||
{
|
||||
QString s;
|
||||
|
||||
public:
|
||||
Gender() :s(/*confused!*/){}
|
||||
|
||||
Gender( const QString& ss ) :s( ss.toLower() )
|
||||
{}
|
||||
|
||||
bool known() const { return male() || female(); }
|
||||
bool male() const { return s == "m"; }
|
||||
bool female() const { return s == "f"; }
|
||||
|
||||
QString toString() const
|
||||
{
|
||||
#define tr QObject::tr
|
||||
QStringList list;
|
||||
if (male())
|
||||
list << tr("boy") << tr("lad") << tr("chap") << tr("guy");
|
||||
else if (female())
|
||||
// I'm not sexist, it's just I'm gutless and couldn't think
|
||||
// of any other non offensive terms for women!
|
||||
list << tr("girl") << tr("lady") << tr("lass");
|
||||
else
|
||||
return tr("person");
|
||||
|
||||
return list.value( QDateTime::currentDateTime().toTime_t() % list.count() );
|
||||
#undef tr
|
||||
}
|
||||
} m_gender;
|
||||
|
||||
Gender m_gender;
|
||||
unsigned short m_age;
|
||||
unsigned int m_scrobbles;
|
||||
QDateTime m_registered;
|
||||
|
34
thirdparty/liblastfm2/src/types/Xspf.cpp
vendored
34
thirdparty/liblastfm2/src/types/Xspf.cpp
vendored
@@ -17,13 +17,22 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with liblastfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "Xspf.h"
|
||||
#include "../core/XmlQuery.h"
|
||||
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
|
||||
lastfm::Xspf::Xspf( const QDomElement& playlist_node )
|
||||
#include "../core/XmlQuery.h"
|
||||
|
||||
#include "Xspf.h"
|
||||
|
||||
|
||||
lastfm::Xspf::Xspf( const QDomElement& playlist_node, QObject* parent )
|
||||
:QObject( parent )
|
||||
{
|
||||
XmlQuery e( playlist_node );
|
||||
|
||||
int expirySeconds = e["link rel=http://www.last.fm/expiry"].text().toInt();
|
||||
QTimer::singleShot( expirySeconds * 1000, this, SLOT(onExpired()));
|
||||
|
||||
m_title = e["title"].text();
|
||||
|
||||
@@ -44,6 +53,23 @@ lastfm::Xspf::Xspf( const QDomElement& playlist_node )
|
||||
t.setAlbum( e["album"].text() );
|
||||
t.setDuration( e["duration"].text().toInt() / 1000 );
|
||||
t.setLoved( e["extension"]["loved"].text() == "1" );
|
||||
m_tracks += t; // outside try block since location is enough basically
|
||||
t.setSource( Track::LastFmRadio );
|
||||
|
||||
QList<QString> contexts;
|
||||
QDomNodeList contextsNodeList = QDomElement(e["extension"]["context"]).childNodes();
|
||||
|
||||
for ( int i = 0 ; i < contextsNodeList.count() ; ++i )
|
||||
contexts.append( contextsNodeList.item(i).toElement().text() );
|
||||
|
||||
if ( contexts.count() > 0 )
|
||||
t.setContext( TrackContext( contextsNodeList.item(0).toElement().tagName(), contexts ) );
|
||||
|
||||
m_tracks << t; // outside try block since location is enough basically
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lastfm::Xspf::onExpired()
|
||||
{
|
||||
emit expired();
|
||||
}
|
||||
|
15
thirdparty/liblastfm2/src/types/Xspf.h
vendored
15
thirdparty/liblastfm2/src/types/Xspf.h
vendored
@@ -25,15 +25,24 @@
|
||||
|
||||
namespace lastfm
|
||||
{
|
||||
class LASTFM_DLLEXPORT Xspf
|
||||
class LASTFM_DLLEXPORT Xspf : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/** pass in the playlist node! */
|
||||
Xspf( const class QDomElement& playlist_node );
|
||||
Xspf( const class QDomElement& playlist_node, QObject* parent );
|
||||
|
||||
QList<Track> tracks() const { return m_tracks; }
|
||||
QString title() const{ return m_title; }
|
||||
|
||||
bool isEmpty() const { return m_tracks.isEmpty(); }
|
||||
Track takeFirst() { return m_tracks.takeFirst(); }
|
||||
|
||||
signals:
|
||||
void expired();
|
||||
|
||||
private slots:
|
||||
void onExpired();
|
||||
|
||||
private:
|
||||
QList<Track> m_tracks;
|
||||
QString m_title;
|
||||
|
@@ -43,6 +43,8 @@ lastfm::InternetConnectionMonitor::InternetConnectionMonitor( QObject *parent )
|
||||
void
|
||||
lastfm::InternetConnectionMonitor::onFinished( QNetworkReply* reply )
|
||||
{
|
||||
if( reply->attribute( QNetworkRequest::SourceIsFromCacheAttribute).toBool() ) return;
|
||||
|
||||
switch( reply->error() )
|
||||
{
|
||||
case QNetworkReply::NoError:
|
||||
@@ -51,6 +53,7 @@ lastfm::InternetConnectionMonitor::onFinished( QNetworkReply* reply )
|
||||
m_up = true;
|
||||
emit up();
|
||||
emit connectivityChanged( m_up );
|
||||
qDebug() << "Internet connection is reachable :)";
|
||||
}
|
||||
break;
|
||||
case QNetworkReply::HostNotFoundError:
|
||||
@@ -78,9 +81,10 @@ lastfm::InternetConnectionMonitor::onNetworkUp()
|
||||
#ifdef Q_OS_MAC
|
||||
// We don't need to check on mac as the
|
||||
// check is done as part of the reach api
|
||||
m_up = true;
|
||||
m_up = true;
|
||||
emit up();
|
||||
emit connectivityChanged( m_up );
|
||||
qDebug() << "Internet connection is reachable :)";
|
||||
#else
|
||||
qDebug() << "Network seems to be up again. Let's try if there's internet connection!";
|
||||
lastfm::nam()->head( QNetworkRequest( QUrl( tr( "http://www.last.fm/" ) ) ) );
|
||||
@@ -90,7 +94,7 @@ lastfm::InternetConnectionMonitor::onNetworkUp()
|
||||
void
|
||||
lastfm::InternetConnectionMonitor::onNetworkDown()
|
||||
{
|
||||
qDebug() << "Internet is down :( boo!!";
|
||||
qDebug() << "Internet is unreachable :(";
|
||||
m_up = false;
|
||||
emit down();
|
||||
emit connectivityChanged( m_up );
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
- Primarily authored by Max Howell, Jono Cole and Doug Mansell
|
||||
|
||||
This file is part of liblastfm.
|
||||
@@ -34,11 +34,11 @@
|
||||
|
||||
static struct NetworkAccessManagerInit
|
||||
{
|
||||
// We do this upfront because then our Firehose QTcpSocket will have a proxy
|
||||
// We do this upfront because then our Firehose QTcpSocket will have a proxy
|
||||
// set by default. As well as any plain QNetworkAcessManager stuff, and the
|
||||
// scrobbler
|
||||
// In theory we should do this every request in case the configuration
|
||||
// changes but that is fairly unlikely use case, init? Maybe we should
|
||||
// In theory we should do this every request in case the configuration
|
||||
// changes but that is fairly unlikely use case, init? Maybe we should
|
||||
// anyway..
|
||||
|
||||
NetworkAccessManagerInit()
|
||||
@@ -50,7 +50,7 @@ static struct NetworkAccessManagerInit
|
||||
// at two seconds, so that hangs startup
|
||||
if (!s.fAutoDetect && s.lpszProxy)
|
||||
{
|
||||
QUrl url( QString::fromUtf16((const unsigned short*)s.lpszProxy) );
|
||||
QUrl url( QString::fromUtf16(s.lpszProxy) );
|
||||
QNetworkProxy proxy( QNetworkProxy::HttpProxy );
|
||||
proxy.setHostName( url.host() );
|
||||
proxy.setPort( url.port() );
|
||||
@@ -69,10 +69,10 @@ static struct NetworkAccessManagerInit
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} init;
|
||||
} init;
|
||||
|
||||
|
||||
namespace lastfm
|
||||
namespace lastfm
|
||||
{
|
||||
LASTFM_DLLEXPORT QByteArray UserAgent;
|
||||
}
|
||||
@@ -106,12 +106,12 @@ lastfm::NetworkAccessManager::~NetworkAccessManager()
|
||||
|
||||
QNetworkProxy
|
||||
lastfm::NetworkAccessManager::proxy( const QNetworkRequest& request )
|
||||
{
|
||||
{
|
||||
Q_UNUSED( request );
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
IeSettings s;
|
||||
if (s.fAutoDetect)
|
||||
if (s.fAutoDetect)
|
||||
{
|
||||
if (!m_pac) {
|
||||
m_pac = new Pac;
|
||||
@@ -122,9 +122,9 @@ lastfm::NetworkAccessManager::proxy( const QNetworkRequest& request )
|
||||
}
|
||||
}
|
||||
return m_pac->resolve( request, s.lpszAutoConfigUrl );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
return QNetworkProxy::applicationProxy();
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ lastfm::NetworkAccessManager::createRequest( Operation op, const QNetworkRequest
|
||||
|
||||
request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
|
||||
request.setRawHeader( "User-Agent", lastfm::UserAgent );
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
// PAC proxies can vary by domain, so we have to check everytime :(
|
||||
QNetworkProxy proxy = this->proxy( request );
|
||||
@@ -152,7 +152,7 @@ void
|
||||
lastfm::NetworkAccessManager::onConnectivityChanged( bool up )
|
||||
{
|
||||
Q_UNUSED( up );
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
if (up && m_pac) m_pac->resetFailedState();
|
||||
#endif
|
||||
|
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
|
||||
#include "MNetworkConnectionMonitor.h"
|
||||
#include "../ws.h"
|
||||
#include "ws/ws.h"
|
||||
|
||||
#include <QPointer>
|
||||
#include <SystemConfiguration/SCNetworkReachability.h>
|
||||
@@ -58,8 +58,6 @@ MNetworkConnectionMonitor::callback( SCNetworkReachabilityRef target,
|
||||
else
|
||||
b = flags & (kSCNetworkFlagsReachable | kSCNetworkFlagsTransientConnection | kSCNetworkFlagsConnectionAutomatic);
|
||||
|
||||
qDebug() << "Can reach " LASTFM_WS_HOSTNAME ":" << b << ", flags:" << flags;
|
||||
|
||||
// basically, avoids telling everyone that we're up already on startup
|
||||
if (up == b)
|
||||
return;
|
||||
|
27
thirdparty/liblastfm2/src/ws/win/ComSetup.h
vendored
27
thirdparty/liblastfm2/src/ws/win/ComSetup.h
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
- Primarily authored by Max Howell, Jono Cole and Doug Mansell
|
||||
|
||||
This file is part of liblastfm.
|
||||
@@ -23,13 +23,12 @@
|
||||
#endif
|
||||
|
||||
#include <objbase.h>
|
||||
//#include <boost/range/atl.hpp>
|
||||
//#include <atlbase.h>
|
||||
//#include <atlcom.h>
|
||||
#include <winable.h>
|
||||
#include <atlbase.h>
|
||||
#include <atlcom.h>
|
||||
|
||||
|
||||
/** @brief WsConnectionMonitor needs Com to work as early as possible so we do this
|
||||
* @author <doug@last.fm>
|
||||
* @author <doug@last.fm>
|
||||
*/
|
||||
class ComSetup
|
||||
{
|
||||
@@ -38,27 +37,27 @@ public:
|
||||
{
|
||||
HRESULT hr = CoInitialize(0);
|
||||
m_bComInitialised = SUCCEEDED(hr);
|
||||
//_ASSERT(m_bComInitialised);
|
||||
_ASSERT(m_bComInitialised);
|
||||
if (m_bComInitialised) {
|
||||
setupSecurity();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setupSecurity()
|
||||
{
|
||||
//CSecurityDescriptor sd;
|
||||
//sd.InitializeFromThreadToken();
|
||||
//HRESULT hr = CoInitializeSecurity(sd, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
|
||||
//_ASSERT(SUCCEEDED(hr));
|
||||
CSecurityDescriptor sd;
|
||||
sd.InitializeFromThreadToken();
|
||||
HRESULT hr = CoInitializeSecurity(sd, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
|
||||
_ASSERT(SUCCEEDED(hr));
|
||||
}
|
||||
|
||||
|
||||
~ComSetup()
|
||||
{
|
||||
if (m_bComInitialised) {
|
||||
CoUninitialize();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
bool m_bComInitialised;
|
||||
};
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
- Primarily authored by Max Howell, Jono Cole and Doug Mansell
|
||||
|
||||
This file is part of liblastfm.
|
||||
@@ -26,7 +26,6 @@
|
||||
*/
|
||||
struct IeSettings : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
|
||||
{
|
||||
#ifndef WIN32
|
||||
IeSettings()
|
||||
{
|
||||
if (!WinHttpGetIEProxyConfigForCurrentUser(this)) {
|
||||
@@ -34,14 +33,11 @@ struct IeSettings : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
|
||||
lpszAutoConfigUrl = lpszProxy = lpszProxyBypass = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
|
||||
~IeSettings()
|
||||
{
|
||||
if (lpszAutoConfigUrl) GlobalFree(lpszAutoConfigUrl);
|
||||
if (lpszProxy) GlobalFree(lpszProxy);
|
||||
if (lpszProxyBypass) GlobalFree(lpszProxyBypass);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
26
thirdparty/liblastfm2/src/ws/win/NdisEvents.cpp
vendored
26
thirdparty/liblastfm2/src/ws/win/NdisEvents.cpp
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
- Primarily authored by Max Howell, Jono Cole and Doug Mansell
|
||||
|
||||
This file is part of liblastfm.
|
||||
@@ -32,37 +32,35 @@ NdisEvents::NdisEvents()
|
||||
|
||||
NdisEvents::~NdisEvents()
|
||||
{
|
||||
#ifndef WIN32
|
||||
if (m_pSink)
|
||||
m_pSink->disconnect();
|
||||
#endif
|
||||
//if (m_pServices && m_pSink)
|
||||
//m_pServices->CancelAsyncCall(m_pSink);
|
||||
if (m_pServices && m_pSink)
|
||||
m_pServices->CancelAsyncCall(m_pSink);
|
||||
// and reference counting will take care of the WmiSink object
|
||||
}
|
||||
|
||||
HRESULT
|
||||
NdisEvents::registerForNdisEvents()
|
||||
{
|
||||
HRESULT hr = 0; //m_pLocator.CoCreateInstance(CLSID_WbemLocator);
|
||||
HRESULT hr = m_pLocator.CoCreateInstance(CLSID_WbemLocator);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
// Connect to the root\wmi namespace with the current user.
|
||||
hr = 0; /*m_pLocator->ConnectServer(CComBSTR("ROOT\\WMI"), // strNetworkResource
|
||||
hr = m_pLocator->ConnectServer(CComBSTR("ROOT\\WMI"), // strNetworkResource
|
||||
NULL, // strUser
|
||||
NULL, // strPassword
|
||||
NULL, // strLocale
|
||||
NULL, // strLocale
|
||||
0, // lSecurityFlags
|
||||
CComBSTR(""), // strAuthority
|
||||
CComBSTR(""), // strAuthority
|
||||
NULL, // pCtx
|
||||
&m_pServices
|
||||
);*/
|
||||
);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
#ifndef WIN32
|
||||
|
||||
m_pSink = new WmiSink(this);
|
||||
#endif
|
||||
|
||||
//////////////////////////
|
||||
|
||||
// other notifications we're not interested in right now include...
|
||||
@@ -77,12 +75,12 @@ NdisEvents::registerForNdisEvents()
|
||||
// MSNdis_StatusProtocolUnbind
|
||||
// MSNdis_StatusMediaSpecificIndication
|
||||
|
||||
/*CComBSTR wql("WQL");
|
||||
CComBSTR wql("WQL");
|
||||
CComBSTR query("SELECT * FROM MSNdis_StatusMediaDisconnect");
|
||||
hr = m_pServices->ExecNotificationQueryAsync(wql, query, 0, 0, m_pSink);
|
||||
|
||||
query = "SELECT * FROM MSNdis_StatusMediaConnect";
|
||||
hr = m_pServices->ExecNotificationQueryAsync(wql, query, 0, 0, m_pSink);*/
|
||||
hr = m_pServices->ExecNotificationQueryAsync(wql, query, 0, 0, m_pSink);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
10
thirdparty/liblastfm2/src/ws/win/NdisEvents.h
vendored
10
thirdparty/liblastfm2/src/ws/win/NdisEvents.h
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
- Primarily authored by Max Howell, Jono Cole and Doug Mansell
|
||||
|
||||
This file is part of liblastfm.
|
||||
@@ -21,8 +21,8 @@
|
||||
#define NDIS_EVENTS_H
|
||||
|
||||
#include <windows.h>
|
||||
//#include <atlbase.h>
|
||||
//#include <WbemCli.h>
|
||||
#include <atlbase.h>
|
||||
#include <WbemCli.h>
|
||||
|
||||
class NdisEvents
|
||||
{
|
||||
@@ -35,8 +35,8 @@ public:
|
||||
virtual void onConnectionDown(BSTR name) = 0;
|
||||
|
||||
private:
|
||||
//CComPtr<IWbemLocator> m_pLocator;
|
||||
//CComPtr<IWbemServices> m_pServices;
|
||||
CComPtr<IWbemLocator> m_pLocator;
|
||||
CComPtr<IWbemServices> m_pServices;
|
||||
class WmiSink *m_pSink;
|
||||
};
|
||||
|
||||
|
40
thirdparty/liblastfm2/src/ws/win/Pac.cpp
vendored
40
thirdparty/liblastfm2/src/ws/win/Pac.cpp
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
- Primarily authored by Max Howell, Jono Cole and Doug Mansell
|
||||
|
||||
This file is part of liblastfm.
|
||||
@@ -21,9 +21,8 @@
|
||||
#include <QNetworkRequest>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
//#include <atlbase.h>
|
||||
//#include <atlconv.h>
|
||||
#include <winhttp.h>
|
||||
#include <atlbase.h>
|
||||
#include <atlconv.h>
|
||||
|
||||
|
||||
static bool
|
||||
@@ -46,7 +45,7 @@ parsePacServer(const QString &s, QNetworkProxy &p)
|
||||
static QList<QNetworkProxy>
|
||||
parsePacResult(const QString &pacResult)
|
||||
{
|
||||
// msdn says: "The proxy server list contains one or more of the
|
||||
// msdn says: "The proxy server list contains one or more of the
|
||||
// following strings separated by semicolons or whitespace."
|
||||
// ([<scheme>=][<scheme>"://"]<server>[":"<port>])
|
||||
|
||||
@@ -74,10 +73,8 @@ lastfm::Pac::Pac()
|
||||
|
||||
lastfm::Pac::~Pac()
|
||||
{
|
||||
#ifndef WIN32
|
||||
if (m_hSession)
|
||||
WinHttpCloseHandle(m_hSession);
|
||||
#endif
|
||||
}
|
||||
|
||||
QNetworkProxy
|
||||
@@ -89,45 +86,42 @@ lastfm::Pac::resolve(const QNetworkRequest &request, const wchar_t* pacUrl)
|
||||
if (!m_hSession)
|
||||
{
|
||||
QByteArray user_agent = request.rawHeader("user-agent");
|
||||
//m_hSession = WinHttpOpen(CA2W(user_agent), WINHTTP_ACCESS_TYPE_NO_PROXY, 0, 0, WINHTTP_FLAG_ASYNC);
|
||||
m_hSession = WinHttpOpen(CA2W(user_agent), WINHTTP_ACCESS_TYPE_NO_PROXY, 0, 0, WINHTTP_FLAG_ASYNC);
|
||||
}
|
||||
if (m_hSession)
|
||||
{
|
||||
WINHTTP_PROXY_INFO info;
|
||||
WINHTTP_AUTOPROXY_OPTIONS opts;
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
if (pacUrl)
|
||||
if (pacUrl)
|
||||
{
|
||||
//opts.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
|
||||
//opts.lpszAutoConfigUrl = pacUrl;
|
||||
}
|
||||
opts.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
|
||||
opts.lpszAutoConfigUrl = pacUrl;
|
||||
}
|
||||
else
|
||||
{
|
||||
//opts.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
|
||||
//opts.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
|
||||
opts.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
|
||||
opts.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
|
||||
}
|
||||
opts.fAutoLogonIfChallenged = TRUE;
|
||||
#ifndef WIN32
|
||||
if (WinHttpGetProxyForUrl(m_hSession, (const WCHAR*)(request.url().toString().utf16()), &opts, &info)) {
|
||||
if (info.lpszProxy)
|
||||
|
||||
if (WinHttpGetProxyForUrl(m_hSession, request.url().toString().utf16(), &opts, &info)) {
|
||||
if (info.lpszProxy)
|
||||
{
|
||||
QList<QNetworkProxy> proxies = parsePacResult(QString::fromUtf16((const ushort*)info.lpszProxy));
|
||||
QList<QNetworkProxy> proxies = parsePacResult(QString::fromUtf16(info.lpszProxy));
|
||||
if (!proxies.empty())
|
||||
{
|
||||
out = proxies.at(0);
|
||||
}
|
||||
// pay attention! casting away constness
|
||||
GlobalFree((void*)info.lpszProxy);
|
||||
GlobalFree(info.lpszProxy);
|
||||
}
|
||||
if (info.lpszProxyBypass)
|
||||
{
|
||||
// pay attention! casting away constness
|
||||
GlobalFree((void*)info.lpszProxyBypass);
|
||||
GlobalFree(info.lpszProxyBypass);
|
||||
}
|
||||
} else {
|
||||
m_bFailed = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return out;
|
||||
|
10
thirdparty/liblastfm2/src/ws/win/Pac.h
vendored
10
thirdparty/liblastfm2/src/ws/win/Pac.h
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
- Primarily authored by Max Howell, Jono Cole and Doug Mansell
|
||||
|
||||
This file is part of liblastfm.
|
||||
@@ -24,14 +24,14 @@
|
||||
#include <windows.h>
|
||||
#include <winhttp.h>
|
||||
class QNetworkRequest;
|
||||
|
||||
|
||||
namespace lastfm
|
||||
{
|
||||
/** @brief simple wrapper to do per url automatic proxy detection
|
||||
/** @brief simple wrapper to do per url automatic proxy detection
|
||||
* @author <doug@last.fm>
|
||||
*/
|
||||
class Pac
|
||||
{
|
||||
{
|
||||
HINTERNET m_hSession;
|
||||
bool m_bFailed;
|
||||
|
||||
@@ -49,4 +49,4 @@ namespace lastfm
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
4
thirdparty/liblastfm2/src/ws/win/WmiSink.h
vendored
4
thirdparty/liblastfm2/src/ws/win/WmiSink.h
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
- Primarily authored by Max Howell, Jono Cole and Doug Mansell
|
||||
|
||||
This file is part of liblastfm.
|
||||
@@ -20,7 +20,7 @@
|
||||
#ifndef WMISINK_WIN_H
|
||||
#define WMISINK_WIN_H
|
||||
|
||||
#include "wbemcli.h"
|
||||
#include "WbemCli.h"
|
||||
|
||||
// Sink object for WMI NDIS notifications
|
||||
class WmiSink : public IWbemObjectSink
|
||||
|
146
thirdparty/liblastfm2/src/ws/ws.cpp
vendored
146
thirdparty/liblastfm2/src/ws/ws.cpp
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
Copyright 2009 Last.fm Ltd.
|
||||
- Primarily authored by Max Howell, Jono Cole and Doug Mansell
|
||||
|
||||
This file is part of liblastfm.
|
||||
@@ -25,15 +25,11 @@
|
||||
#include <QDomElement>
|
||||
#include <QLocale>
|
||||
#include <QStringList>
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
#include <QUrl>
|
||||
static QNetworkAccessManager* nam = 0;
|
||||
|
||||
static QMap< QThread*, QNetworkAccessManager* > threadNamHash;
|
||||
static QSet< QThread* > ourNamSet;
|
||||
static QMutex namAccessMutex;
|
||||
|
||||
QString
|
||||
QString
|
||||
lastfm::ws::host()
|
||||
{
|
||||
QStringList const args = QCoreApplication::arguments();
|
||||
@@ -47,18 +43,18 @@ lastfm::ws::host()
|
||||
return LASTFM_WS_HOSTNAME;
|
||||
}
|
||||
|
||||
static QUrl url()
|
||||
static QUrl baseUrl()
|
||||
{
|
||||
QUrl url;
|
||||
url.setScheme( "http" );
|
||||
url.setHost( lastfm::ws::host() );
|
||||
url.setPath( "/2.0/" );
|
||||
url.setEncodedPath( "/2.0/" );
|
||||
return url;
|
||||
}
|
||||
|
||||
static QString iso639()
|
||||
{
|
||||
return QLocale().name().left( 2 ).toLower();
|
||||
{
|
||||
return QLocale().name().left( 2 ).toLower();
|
||||
}
|
||||
|
||||
void autograph( QMap<QString, QString>& params )
|
||||
@@ -86,11 +82,11 @@ static void sign( QMap<QString, QString>& params, bool sk = true )
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply*
|
||||
lastfm::ws::get( QMap<QString, QString> params )
|
||||
QUrl
|
||||
lastfm::ws::url( QMap<QString, QString> params )
|
||||
{
|
||||
sign( params );
|
||||
QUrl url = ::url();
|
||||
QUrl url = ::baseUrl();
|
||||
// Qt setQueryItems doesn't encode a bunch of stuff, so we do it manually
|
||||
QMapIterator<QString, QString> i( params );
|
||||
while (i.hasNext()) {
|
||||
@@ -100,16 +96,21 @@ lastfm::ws::get( QMap<QString, QString> params )
|
||||
url.addEncodedQueryItem( key, value );
|
||||
}
|
||||
|
||||
qDebug() << url;
|
||||
return url;
|
||||
}
|
||||
|
||||
return nam()->get( QNetworkRequest(url) );
|
||||
|
||||
QNetworkReply*
|
||||
lastfm::ws::get( QMap<QString, QString> params )
|
||||
{
|
||||
return nam()->get( QNetworkRequest( url( params ) ) );
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply*
|
||||
lastfm::ws::post( QMap<QString, QString> params, bool sk )
|
||||
{
|
||||
sign( params, sk );
|
||||
{
|
||||
sign( params, sk );
|
||||
QByteArray query;
|
||||
QMapIterator<QString, QString> i( params );
|
||||
while (i.hasNext()) {
|
||||
@@ -120,117 +121,24 @@ lastfm::ws::post( QMap<QString, QString> params, bool sk )
|
||||
+ '&';
|
||||
}
|
||||
|
||||
QNetworkRequest req(url());
|
||||
req.setHeader( QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded" );
|
||||
return nam()->post( req, query );
|
||||
}
|
||||
|
||||
|
||||
QByteArray
|
||||
lastfm::ws::parse( QNetworkReply* reply ) throw( ParseError )
|
||||
{
|
||||
try
|
||||
{
|
||||
QByteArray data = reply->readAll();
|
||||
|
||||
if (!data.size())
|
||||
throw MalformedResponse;
|
||||
|
||||
QDomDocument xml;
|
||||
xml.setContent( data );
|
||||
QDomElement lfm = xml.documentElement();
|
||||
|
||||
if (lfm.isNull())
|
||||
throw MalformedResponse;
|
||||
|
||||
QString const status = lfm.attribute( "status" );
|
||||
QDomElement error = lfm.firstChildElement( "error" );
|
||||
uint const n = lfm.childNodes().count();
|
||||
|
||||
// no elements beyond the lfm is perfectably acceptable <-- wtf?
|
||||
// if (n == 0) // nothing useful in the response
|
||||
if (status == "failed" || (n == 1 && !error.isNull()) )
|
||||
throw error.isNull()
|
||||
? MalformedResponse
|
||||
: Error( error.attribute( "code" ).toUInt() );
|
||||
|
||||
switch (reply->error())
|
||||
{
|
||||
case QNetworkReply::RemoteHostClosedError:
|
||||
case QNetworkReply::ConnectionRefusedError:
|
||||
case QNetworkReply::TimeoutError:
|
||||
case QNetworkReply::ContentAccessDenied:
|
||||
case QNetworkReply::ContentOperationNotPermittedError:
|
||||
case QNetworkReply::UnknownContentError:
|
||||
case QNetworkReply::ProtocolInvalidOperationError:
|
||||
case QNetworkReply::ProtocolFailure:
|
||||
throw TryAgainLater;
|
||||
|
||||
case QNetworkReply::NoError:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//FIXME pretty wasteful to parse XML document twice..
|
||||
return data;
|
||||
}
|
||||
catch (Error e)
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
case OperationFailed:
|
||||
case InvalidApiKey:
|
||||
case InvalidSessionKey:
|
||||
// NOTE will never be received during the LoginDialog stage
|
||||
// since that happens before this slot is registered with
|
||||
// QMetaObject in App::App(). Neat :)
|
||||
QMetaObject::invokeMethod( qApp, "onWsError", Q_ARG( lastfm::ws::Error, e ) );
|
||||
default:
|
||||
throw ParseError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// bit dodgy, but prolly for the best
|
||||
reply->deleteLater();
|
||||
return nam()->post( QNetworkRequest(baseUrl()), query );
|
||||
}
|
||||
|
||||
|
||||
QNetworkAccessManager*
|
||||
lastfm::nam()
|
||||
{
|
||||
QMutexLocker l( &namAccessMutex );
|
||||
QThread* thread = QThread::currentThread();
|
||||
if ( !threadNamHash.contains( thread ) )
|
||||
{
|
||||
NetworkAccessManager* newNam = new NetworkAccessManager();
|
||||
threadNamHash[thread] = newNam;
|
||||
ourNamSet.insert( thread );
|
||||
return newNam;
|
||||
}
|
||||
return threadNamHash[thread];
|
||||
{
|
||||
if (!::nam) ::nam = new NetworkAccessManager( qApp );
|
||||
return ::nam;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lastfm::setNetworkAccessManager( QNetworkAccessManager* nam )
|
||||
{
|
||||
if ( !nam )
|
||||
return;
|
||||
|
||||
QMutexLocker l( &namAccessMutex );
|
||||
QThread* thread = QThread::currentThread();
|
||||
QNetworkAccessManager* oldNam = 0;
|
||||
if ( threadNamHash.contains( thread ) && ourNamSet.contains( thread ) )
|
||||
oldNam = threadNamHash[thread];
|
||||
|
||||
if ( oldNam == nam )
|
||||
return;
|
||||
|
||||
threadNamHash[thread] = nam;
|
||||
ourNamSet.remove( thread );
|
||||
|
||||
if ( oldNam )
|
||||
delete oldNam;
|
||||
delete ::nam;
|
||||
::nam = nam;
|
||||
nam->setParent( qApp ); // ensure it isn't deleted out from under us
|
||||
}
|
||||
|
||||
|
||||
@@ -288,7 +196,7 @@ namespace lastfm
|
||||
const char* ApiKey;
|
||||
|
||||
/** if this is found set to "" we conjure ourselves a suitable one */
|
||||
const char* UserAgent = 0;
|
||||
const char* UserAgent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
26
thirdparty/liblastfm2/src/ws/ws.h
vendored
26
thirdparty/liblastfm2/src/ws/ws.h
vendored
@@ -112,6 +112,7 @@ namespace lastfm
|
||||
LASTFM_DLLEXPORT QString host();
|
||||
|
||||
/** the map needs a method entry, as per http://last.fm/api */
|
||||
LASTFM_DLLEXPORT QUrl url( QMap<QString, QString> );
|
||||
LASTFM_DLLEXPORT QNetworkReply* get( QMap<QString, QString> );
|
||||
/** generates api sig, includes api key, and posts, don't add the api
|
||||
* key yourself as well--it'll break */
|
||||
@@ -121,29 +122,16 @@ namespace lastfm
|
||||
class ParseError : public std::runtime_error
|
||||
{
|
||||
Error e;
|
||||
QString m_message;
|
||||
public:
|
||||
explicit ParseError(Error e) : std::runtime_error("lastfm::ws::Error"), e(e)
|
||||
explicit ParseError( Error e, QString message )
|
||||
:std::runtime_error("lastfm::ws::Error"), e(e), m_message(message)
|
||||
{}
|
||||
Error enumValue() const { return e; }
|
||||
};
|
||||
QString message() const { return m_message; }
|
||||
|
||||
/** Generally you don't use this, eg. if you called Artist::getInfo(),
|
||||
* use the Artist::getInfo( QNetworkReply* ) function to get the
|
||||
* results, you have to pass a QDomDocument because QDomElements stop
|
||||
* existing when the parent DomDocument is deleted.
|
||||
*
|
||||
* The QByteArray is basically reply->readAll(), so all this function
|
||||
* does is sanity check the response and throw if it is bad.
|
||||
*
|
||||
* Thus if you don't care about errors just do: reply->readAll()
|
||||
*
|
||||
* Not caring about errors is often fine with Qt as you just get null
|
||||
* strings and that instead, and you can handle those as you go.
|
||||
*
|
||||
* The QByteArray is an XML document. You can parse it with QDom or
|
||||
* use our much more convenient lastfm::XmlQuery.
|
||||
*/
|
||||
LASTFM_DLLEXPORT QByteArray parse( QNetworkReply* reply ) throw( ParseError );
|
||||
~ParseError() throw() {;}
|
||||
};
|
||||
|
||||
/** returns the expiry date of this HTTP response */
|
||||
LASTFM_DLLEXPORT QDateTime expires( QNetworkReply* );
|
||||
|
Reference in New Issue
Block a user