diff --git a/CMakeLists.txt b/CMakeLists.txt index 941ce321c..69c873377 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ include(cmake/Version.cmake) include(cmake/Deb.cmake) include(cmake/Rpm.cmake) include(cmake/SipBindings.cmake) +include(cmake/SpotifyVersion.cmake) if (UNIX AND NOT APPLE) set(LINUX 1) @@ -137,6 +138,16 @@ if(${CMAKE_BUILD_TYPE} MATCHES "Release") add_definitions(-DQT_NO_DEBUG_OUTPUT) endif(${CMAKE_BUILD_TYPE} MATCHES "Release") +# Use protobuf-lite if it's available +find_library(PROTOBUF_LITE_LIBRARY protobuf-lite) +if(NOT PROTOBUF_LITE_LIBRARY) + # Lucid doesn't have a .so symlink + find_file(PROTOBUF_LITE_LIBRARY + NAMES libprotobuf-lite.so.5 + PATH_SUFFIXES lib + ) +endif(NOT PROTOBUF_LITE_LIBRARY) + # Set up definitions and paths add_definitions(${QT_DEFINITIONS}) link_directories(${TAGLIB_LIBRARY_DIRS}) @@ -188,7 +199,8 @@ option(ENABLE_SCRIPTING_ARCHIVES "Enable support for loading scripts from archiv option(ENABLE_SCRIPTING_PYTHON "Enable python scripting" ON) option(ENABLE_REMOTE "Enable support for using remote controls with Clementine" OFF) option(ENABLE_BREAKPAD "Enable crash reporting" OFF) -option(ENABLE_SPOTIFY "Enable spotify support" OFF) +option(ENABLE_SPOTIFY_BLOB "Build the spotify non-GPL binary" ON) +option(ENABLE_SPOTIFY "Enable spotify support" ON) if(WIN32) option(ENABLE_WIN32_CONSOLE "Show the windows console even outside Debug mode" OFF) @@ -234,9 +246,13 @@ if(ENABLE_BREAKPAD) set(HAVE_BREAKPAD ON) endif(ENABLE_BREAKPAD) -if(ENABLE_SPOTIFY AND SPOTIFY_FOUND AND PROTOBUF_FOUND) +if(ENABLE_SPOTIFY AND PROTOBUF_FOUND) set(HAVE_SPOTIFY ON) -endif(ENABLE_SPOTIFY AND SPOTIFY_FOUND AND PROTOBUF_FOUND) +endif(ENABLE_SPOTIFY AND PROTOBUF_FOUND) + +if(ENABLE_SPOTIFY_BLOB AND PROTOBUF_FOUND AND SPOTIFY_FOUND) + set(HAVE_SPOTIFY_BLOB ON) +endif(ENABLE_SPOTIFY_BLOB AND PROTOBUF_FOUND AND SPOTIFY_FOUND) if(ENABLE_VISUALISATIONS) @@ -375,9 +391,13 @@ if(HAVE_BREAKPAD) endif(HAVE_BREAKPAD) if(HAVE_SPOTIFY) - add_subdirectory(spotifyblob) + add_subdirectory(spotifyblob/common) endif(HAVE_SPOTIFY) +if(HAVE_SPOTIFY_BLOB) + add_subdirectory(spotifyblob/blob) +endif(HAVE_SPOTIFY_BLOB) + # Uninstall support configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" @@ -388,20 +408,21 @@ add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") # Show a summary of what we have enabled -summary_add("devices: DeviceKit backend" HAVE_DEVICEKIT) -summary_add("devices: iPod classic support" HAVE_LIBGPOD) -summary_add("devices: iPod Touch, iPhone, iPad support" HAVE_IMOBILEDEVICE) -summary_add("devices: MTP support" HAVE_LIBMTP) -summary_add("devices: GIO backend" HAVE_GIO) -summary_add("D-Bus support" HAVE_DBUS) -summary_add("Gnome sound menu integration" HAVE_LIBINDICATE) -summary_add("Wiimote support" HAVE_WIIMOTEDEV) -summary_add("Visualisations" ENABLE_VISUALISATIONS) -summary_add("Last.fm support" HAVE_LIBLASTFM) summary_add("Crash reporting" HAVE_BREAKPAD) +summary_add("D-Bus support" HAVE_DBUS) +summary_add("Devices: DeviceKit backend" HAVE_DEVICEKIT) +summary_add("Devices: iPod classic support" HAVE_LIBGPOD) +summary_add("Devices: iPod Touch, iPhone, iPad support" HAVE_IMOBILEDEVICE) +summary_add("Devices: MTP support" HAVE_LIBMTP) +summary_add("Devices: GIO backend" HAVE_GIO) +summary_add("Gnome sound menu integration" HAVE_LIBINDICATE) +summary_add("Last.fm support" HAVE_LIBLASTFM) summary_add("Scripting support: loading archives" HAVE_LIBARCHIVE) summary_add("Scripting support: Python" HAVE_SCRIPTING_PYTHON) +summary_add("Spotify support: core code" HAVE_SPOTIFY) +summary_add("Spotify support: non-GPL binary helper" HAVE_SPOTIFY_BLOB) +summary_add("Visualisations" ENABLE_VISUALISATIONS) +summary_add("Wiimote support" HAVE_WIIMOTEDEV) summary_add("(Mac OS X) Sparkle integration" HAVE_SPARKLE) summary_add("(unstable) Remote control support" HAVE_REMOTE) -summary_add("(unstable) Spotify support" HAVE_SPOTIFY) summary_show() diff --git a/cmake/SpotifyVersion.cmake b/cmake/SpotifyVersion.cmake new file mode 100644 index 000000000..7cf6bc0b3 --- /dev/null +++ b/cmake/SpotifyVersion.cmake @@ -0,0 +1,2 @@ +# Increment this whenever the user needs to download a new blob +set(SPOTIFY_BLOB_VERSION 1) diff --git a/spotifyblob/blob/CMakeLists.txt b/spotifyblob/blob/CMakeLists.txt new file mode 100644 index 000000000..4a69eb559 --- /dev/null +++ b/spotifyblob/blob/CMakeLists.txt @@ -0,0 +1,54 @@ +include_directories(${SPOTIFY_INCLUDE_DIRS}) +include_directories(${PROTOBUF_INCLUDE_DIRS}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/../common) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common) +include_directories(${CMAKE_SOURCE_DIR}/src) + +link_directories(${SPOTIFY_LIBRARY_DIRS}) + +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) + +set(SOURCES + main.cpp + spotifyclient.cpp + + ${CMAKE_SOURCE_DIR}/src/core/logging.cpp +) + +set(HEADERS + spotifyclient.h +) + +qt4_wrap_cpp(MOC ${HEADERS}) + +add_executable(clementine-spotifyblob + ${SOURCES} + ${MOC} +) + +target_link_libraries(clementine-spotifyblob + ${SPOTIFY_LIBRARIES} + ${QT_QTCORE_LIBRARY} + ${QT_QTNETWORK_LIBRARY} + clementine-spotifyblob-messages +) + +install(TARGETS clementine-spotifyblob + RUNTIME DESTINATION bin +) + +if(LINUX) + # Versioned name of the blob + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(SPOTIFY_BLOB_ARCH 32) + else(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(SPOTIFY_BLOB_ARCH 64) + endif(CMAKE_SIZEOF_VOID_P EQUAL 4) + + install( + FILES ${CMAKE_BINARY_DIR}/clementine-spotifyblob + DESTINATION ${CMAKE_BINARY_DIR}/spotify/version${SPOTIFY_BLOB_VERSION}-${SPOTIFY_BLOB_ARCH}bit/blob + RENAME ${SPOTIFY_BLOB_NAME} + ) +endif(LINUX) diff --git a/spotifyblob/main.cpp b/spotifyblob/blob/main.cpp similarity index 100% rename from spotifyblob/main.cpp rename to spotifyblob/blob/main.cpp diff --git a/spotifyblob/spotifyclient.cpp b/spotifyblob/blob/spotifyclient.cpp similarity index 100% rename from spotifyblob/spotifyclient.cpp rename to spotifyblob/blob/spotifyclient.cpp diff --git a/spotifyblob/spotifyclient.h b/spotifyblob/blob/spotifyclient.h similarity index 100% rename from spotifyblob/spotifyclient.h rename to spotifyblob/blob/spotifyclient.h diff --git a/spotifyblob/spotifykey.h b/spotifyblob/blob/spotifykey.h similarity index 100% rename from spotifyblob/spotifykey.h rename to spotifyblob/blob/spotifykey.h diff --git a/spotifyblob/CMakeLists.txt b/spotifyblob/common/CMakeLists.txt similarity index 56% rename from spotifyblob/CMakeLists.txt rename to spotifyblob/common/CMakeLists.txt index d0a924acf..0dddd8d1a 100644 --- a/spotifyblob/CMakeLists.txt +++ b/spotifyblob/common/CMakeLists.txt @@ -1,12 +1,9 @@ -include_directories(${SPOTIFY_INCLUDE_DIRS}) include_directories(${PROTOBUF_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_SOURCE_DIR}/src) -link_directories(${SPOTIFY_LIBRARY_DIRS}) - -set(EXECUTABLE_OUTPUT_PATH ..) - +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/blobversion.h.in + ${CMAKE_CURRENT_BINARY_DIR}/blobversion.h) set(COMMON_SOURCES spotifymessagehandler.cpp @@ -29,32 +26,15 @@ add_library(clementine-spotifyblob-messages STATIC ${PROTO_SOURCES} ) +# Use protobuf-lite if it's available +if(PROTOBUF_LITE_LIBRARY) + set(protobuf ${PROTOBUF_LITE_LIBRARY}) +else(PROTOBUF_LITE_LIBRARY) + set(protobuf ${PROTOBUF_LIBRARY}) +endif(PROTOBUF_LITE_LIBRARY) + target_link_libraries(clementine-spotifyblob-messages - ${PROTOBUF_LIBRARY} + ${protobuf} ${CMAKE_THREAD_LIBS_INIT} ) - -set(SOURCES - main.cpp - spotifyclient.cpp - - ../src/core/logging.cpp -) - -set(HEADERS - spotifyclient.h -) - -qt4_wrap_cpp(MOC ${HEADERS}) - -add_executable(clementine-spotifyblob - ${SOURCES} - ${MOC} -) - -target_link_libraries(clementine-spotifyblob - ${SPOTIFY_LIBRARIES} - ${QT_LIBRARIES} - clementine-spotifyblob-messages -) diff --git a/spotifyblob/common/blobversion.h.in b/spotifyblob/common/blobversion.h.in new file mode 100644 index 000000000..0dcf6bb3b --- /dev/null +++ b/spotifyblob/common/blobversion.h.in @@ -0,0 +1,23 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#ifndef SPOTIFY_BLOBVERSION_H_IN +#define SPOTIFY_BLOBVERSION_H_IN + +#define SPOTIFY_BLOB_VERSION ${SPOTIFY_BLOB_VERSION} + +#endif // SPOTIFY_BLOBVERSION_H_IN diff --git a/spotifyblob/spotifymessagehandler.cpp b/spotifyblob/common/spotifymessagehandler.cpp similarity index 95% rename from spotifyblob/spotifymessagehandler.cpp rename to spotifyblob/common/spotifymessagehandler.cpp index 4a84850e2..72e8867b7 100644 --- a/spotifyblob/spotifymessagehandler.cpp +++ b/spotifyblob/common/spotifymessagehandler.cpp @@ -58,7 +58,6 @@ void SpotifyMessageHandler::DeviceReadyRead() { return; } - qLog(Debug) << message.DebugString().c_str(); emit MessageArrived(message); // Clear the buffer @@ -71,8 +70,6 @@ void SpotifyMessageHandler::DeviceReadyRead() { } void SpotifyMessageHandler::SendMessage(const protobuf::SpotifyMessage& message) { - qLog(Debug) << message.DebugString().c_str(); - std::string data(message.SerializeAsString()); QDataStream s(device_); diff --git a/spotifyblob/spotifymessagehandler.h b/spotifyblob/common/spotifymessagehandler.h similarity index 100% rename from spotifyblob/spotifymessagehandler.h rename to spotifyblob/common/spotifymessagehandler.h diff --git a/spotifyblob/spotifymessages.proto b/spotifyblob/common/spotifymessages.proto similarity index 98% rename from spotifyblob/spotifymessages.proto rename to spotifyblob/common/spotifymessages.proto index aa09d9111..524573534 100644 --- a/spotifyblob/spotifymessages.proto +++ b/spotifyblob/common/spotifymessages.proto @@ -21,6 +21,9 @@ package protobuf; +option optimize_for = LITE_RUNTIME; + + message LoginRequest { required string username = 1; required string password = 2; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2ddcc19e1..622e40c2f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -624,6 +624,7 @@ endif(HAVE_LIBLASTFM) if(HAVE_SPOTIFY) list(APPEND SOURCES + radio/spotifyblobdownloader.cpp radio/spotifyconfig.cpp radio/spotifyserver.cpp radio/spotifyservice.cpp @@ -631,6 +632,7 @@ if(HAVE_SPOTIFY) radio/spotifyurlhandler.cpp ) list(APPEND HEADERS + radio/spotifyblobdownloader.h radio/spotifyconfig.h radio/spotifyserver.h radio/spotifyservice.h @@ -1023,8 +1025,6 @@ endif(HAVE_BREAKPAD) if(HAVE_SPOTIFY) target_link_libraries(clementine_lib clementine-spotifyblob-messages) - add_dependencies(clementine_lib - clementine-spotifyblob) endif(HAVE_SPOTIFY) if (APPLE) diff --git a/src/config.h.in b/src/config.h.in index 3eba0d2e3..8f7839ec2 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -18,6 +18,7 @@ #define CONFIG_H_IN #define CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" +#define CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}" #cmakedefine ENABLE_VISUALISATIONS #cmakedefine HAVE_BREAKPAD diff --git a/src/core/logging.cpp b/src/core/logging.cpp index 330ea07b0..9b2885902 100644 --- a/src/core/logging.cpp +++ b/src/core/logging.cpp @@ -34,7 +34,7 @@ static Level sDefaultLevel = Level_Debug; static QMap* sClassLevels = NULL; static QIODevice* sNullDevice = NULL; -const char* kDefaultLogLevels = "GstEnginePipeline:2,SpotifyMessageHandler:2,*:3"; +const char* kDefaultLogLevels = "GstEnginePipeline:2,*:3"; static const char* kMessageHandlerMagic = "__logging_message__"; static const int kMessageHandlerMagicLength = strlen(kMessageHandlerMagic); diff --git a/src/core/utilities.cpp b/src/core/utilities.cpp index 78bdd84e8..d5515198b 100644 --- a/src/core/utilities.cpp +++ b/src/core/utilities.cpp @@ -273,6 +273,9 @@ QString GetConfigPath(ConfigPath config) { return QDir::homePath(); #endif + case Path_LocalSpotifyBlob: + return GetConfigPath(Path_Root) + "/spotifyblob"; + default: qFatal("%s", Q_FUNC_INFO); return QString::null; diff --git a/src/core/utilities.h b/src/core/utilities.h index dd9e4ee3a..8c1116195 100644 --- a/src/core/utilities.h +++ b/src/core/utilities.h @@ -55,6 +55,7 @@ namespace Utilities { Path_GstreamerRegistry, Path_DefaultMusicLibrary, Path_Scripts, + Path_LocalSpotifyBlob, }; QString GetConfigPath(ConfigPath config); } diff --git a/src/radio/spotifyblobdownloader.cpp b/src/radio/spotifyblobdownloader.cpp new file mode 100644 index 000000000..3c1e27a9a --- /dev/null +++ b/src/radio/spotifyblobdownloader.cpp @@ -0,0 +1,137 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#include "spotifyblobdownloader.h" +#include "spotifyservice.h" +#include "core/logging.h" +#include "core/network.h" + +#include +#include +#include +#include + +SpotifyBlobDownloader::SpotifyBlobDownloader( + const QString& version, const QString& path, QObject* parent) + : QObject(parent), + version_(version), + path_(path), + network_(new NetworkAccessManager(this)), + progress_(new QProgressDialog(tr("Downloading Spotify plugin"), tr("Cancel"), 0, 0)) +{ + progress_->setWindowTitle(QCoreApplication::applicationName()); + connect(progress_, SIGNAL(canceled()), SLOT(Cancel())); +} + +SpotifyBlobDownloader::~SpotifyBlobDownloader() { + qDeleteAll(replies_); + replies_.clear(); + + delete progress_; +} + +void SpotifyBlobDownloader::Start() { + qDeleteAll(replies_); + replies_.clear(); + + const QStringList filenames = QStringList() << "blob" << "libspotify.so.7"; + + foreach (const QString& filename, filenames) { + const QUrl url(SpotifyService::kBlobDownloadUrl + version_ + "/" + filename); + qLog(Info) << "Downloading" << url; + + QNetworkReply* reply = network_->get(QNetworkRequest(url)); + connect(reply, SIGNAL(finished()), SLOT(ReplyFinished())); + connect(reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(ReplyProgress())); + + replies_ << reply; + } + + progress_->show(); +} + +void SpotifyBlobDownloader::ReplyFinished() { + QNetworkReply* reply = qobject_cast(sender()); + if (reply->error() != QNetworkReply::NoError) { + // Handle network errors + ShowError(reply->errorString()); + return; + } + + // Is everything finished? + foreach (QNetworkReply* reply, replies_) { + if (!reply->isFinished()) { + return; + } + } + + // Make the destination directory and write the files into it + QDir().mkpath(path_); + + foreach (QNetworkReply* reply, replies_) { + const QString filename = reply->url().path().section('/', -1, -1); + const QString path = path_ + "/" + filename; + + qLog(Info) << "Saving file" << path; + QFile file(path); + if (!file.open(QIODevice::WriteOnly)) { + ShowError("Failed to open file for writing: " + path); + return; + } + + file.setPermissions(QFile::Permissions(0x7755)); + file.write(reply->readAll()); + } + + EmitFinished(); +} + +void SpotifyBlobDownloader::ReplyProgress() { + int progress = 0; + int total = 0; + + foreach (QNetworkReply* reply, replies_) { + progress += reply->bytesAvailable(); + total += reply->rawHeader("Content-Length").toInt(); + } + + progress_->setMaximum(total); + progress_->setValue(progress); +} + +void SpotifyBlobDownloader::Cancel() { + deleteLater(); +} + +void SpotifyBlobDownloader::ShowError(const QString& message) { + // Stop any remaining replies before showing the dialog so they don't + // carry on in the background + foreach (QNetworkReply* reply, replies_) { + disconnect(reply, 0, this, 0); + reply->abort(); + } + + qLog(Warning) << message; + QMessageBox::warning(NULL, tr("Error downloading Spotify plugin"), message, + QMessageBox::Close); + deleteLater(); +} + +void SpotifyBlobDownloader::EmitFinished() { + emit Finished(); + deleteLater(); +} diff --git a/src/radio/spotifyblobdownloader.h b/src/radio/spotifyblobdownloader.h new file mode 100644 index 000000000..f69c58778 --- /dev/null +++ b/src/radio/spotifyblobdownloader.h @@ -0,0 +1,59 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#ifndef SPOTIFYBLOBDOWNLOADER_H +#define SPOTIFYBLOBDOWNLOADER_H + +#include + +class QNetworkAccessManager; +class QNetworkReply; +class QProgressDialog; + +class SpotifyBlobDownloader : public QObject { + Q_OBJECT + +public: + SpotifyBlobDownloader(const QString& version, const QString& path, + QObject* parent = 0); + ~SpotifyBlobDownloader(); + + void Start(); + +signals: + void Finished(); + +private slots: + void ReplyFinished(); + void ReplyProgress(); + void Cancel(); + +private: + void ShowError(const QString& message); + void EmitFinished(); + +private: + QString version_; + QString path_; + + QNetworkAccessManager* network_; + QList replies_; + + QProgressDialog* progress_; +}; + +#endif // SPOTIFYBLOBDOWNLOADER_H diff --git a/src/radio/spotifyserver.cpp b/src/radio/spotifyserver.cpp index 4ac3e4c59..7d34f1764 100644 --- a/src/radio/spotifyserver.cpp +++ b/src/radio/spotifyserver.cpp @@ -18,8 +18,8 @@ #include "spotifyserver.h" #include "core/logging.h" -#include "spotifyblob/spotifymessages.pb.h" -#include "spotifyblob/spotifymessagehandler.h" +#include "spotifyblob/common/spotifymessages.pb.h" +#include "spotifyblob/common/spotifymessagehandler.h" #include #include diff --git a/src/radio/spotifyserver.h b/src/radio/spotifyserver.h index 9bb2aa0b2..a97e888f8 100644 --- a/src/radio/spotifyserver.h +++ b/src/radio/spotifyserver.h @@ -18,7 +18,7 @@ #ifndef SPOTIFYSERVER_H #define SPOTIFYSERVER_H -#include "spotifyblob/spotifymessages.pb.h" +#include "spotifyblob/common/spotifymessages.pb.h" #include #include diff --git a/src/radio/spotifyservice.cpp b/src/radio/spotifyservice.cpp index cd1319214..04d115e4e 100644 --- a/src/radio/spotifyservice.cpp +++ b/src/radio/spotifyservice.cpp @@ -1,4 +1,6 @@ +#include "config.h" #include "radiomodel.h" +#include "spotifyblobdownloader.h" #include "spotifyserver.h" #include "spotifyservice.h" #include "spotifysearchplaylisttype.h" @@ -7,20 +9,26 @@ #include "core/logging.h" #include "core/player.h" #include "core/taskmanager.h" +#include "core/utilities.h" #include "playlist/playlist.h" #include "playlist/playlistcontainer.h" #include "playlist/playlistmanager.h" -#include "spotifyblob/spotifymessagehandler.h" +#include "spotifyblob/common/blobversion.h" +#include "spotifyblob/common/spotifymessagehandler.h" #include "widgets/didyoumean.h" #include "ui/iconloader.h" #include +#include +#include #include +#include #include #include const char* SpotifyService::kServiceName = "Spotify"; const char* SpotifyService::kSettingsGroup = "Spotify"; +const char* SpotifyService::kBlobDownloadUrl = "http://spotify.clementine-player.org/"; const int SpotifyService::kSearchDelayMsec = 400; SpotifyService::SpotifyService(RadioModel* parent) @@ -35,12 +43,18 @@ SpotifyService::SpotifyService(RadioModel* parent) pending_search_playlist_(NULL), context_menu_(NULL), search_delay_(new QTimer(this)) { -#ifdef Q_OS_DARWIN - blob_path_ = QCoreApplication::applicationDirPath() + "/../PlugIns/clementine-spotifyblob"; -#else - blob_path_ = QCoreApplication::applicationFilePath() + "-spotifyblob"; -#endif - qLog(Debug) << "Loading spotify blob from:" << blob_path_; + // Build the search path for the binary blob. + // Look for one distributed alongside clementine first, then check in the + // user's home directory for any that have been downloaded. + blob_path_ << QCoreApplication::applicationFilePath() + "-spotifyblob" CMAKE_EXECUTABLE_SUFFIX + << QCoreApplication::applicationDirPath() + "/../PlugIns/clementine-spotifyblob" CMAKE_EXECUTABLE_SUFFIX; + + local_blob_version_ = QString("version%1-%2bit").arg(SPOTIFY_BLOB_VERSION).arg(sizeof(void*) * 8); + local_blob_path_ = Utilities::GetConfigPath(Utilities::Path_LocalSpotifyBlob) + + "/" + local_blob_version_ + "/blob"; + + qLog(Debug) << "Spotify blob search path:" << blob_path_; + qLog(Debug) << "Spotify local blob path:" << local_blob_path_; model()->player()->RegisterUrlHandler(url_handler_); model()->player()->playlists()->RegisterSpecialPlaylistType( @@ -115,19 +129,18 @@ void SpotifyService::LoginCompleted(bool success) { void SpotifyService::BlobProcessError(QProcess::ProcessError error) { qLog(Error) << "Spotify blob process failed:" << error; + blob_process_->deleteLater(); + blob_process_ = NULL; } void SpotifyService::EnsureServerCreated(const QString& username, const QString& password) { - if (server_) { + if (server_ && blob_process_) { return; } - qLog(Debug) << Q_FUNC_INFO; - + delete server_; server_ = new SpotifyServer(this); - blob_process_ = new QProcess(this); - blob_process_->setProcessChannelMode(QProcess::ForwardedChannels); connect(server_, SIGNAL(LoginCompleted(bool)), SLOT(LoginCompleted(bool))); connect(server_, SIGNAL(PlaylistsUpdated(protobuf::Playlists)), @@ -145,13 +158,7 @@ void SpotifyService::EnsureServerCreated(const QString& username, connect(server_, SIGNAL(ImageLoaded(QString,QImage)), SLOT(ImageLoaded(QString,QImage))); - connect(blob_process_, - SIGNAL(error(QProcess::ProcessError)), - SLOT(BlobProcessError(QProcess::ProcessError))); - server_->Init(); - blob_process_->start( - blob_path_, QStringList() << QString::number(server_->server_port())); login_task_id_ = model()->task_manager()->StartTask(tr("Connecting to Spotify")); @@ -163,6 +170,71 @@ void SpotifyService::EnsureServerCreated(const QString& username, } else { server_->Login(username, password); } + + StartBlobProcess(); +} + +void SpotifyService::StartBlobProcess() { + // Try to find an executable to run + QString blob_path; + QProcessEnvironment env(QProcessEnvironment::systemEnvironment()); + + // Look in the system search path first + foreach (const QString& path, blob_path_) { + if (QFile::exists(path)) { + blob_path = path; + break; + } + } + + // Next look in the local path + const QString local_blob_dir = QFileInfo(local_blob_path_).path(); + if (blob_path.isEmpty()) { + if (QFile::exists(local_blob_path_)) { + blob_path = local_blob_path_; + env.insert("LD_LIBRARY_PATH", local_blob_dir); + } + } + + if (blob_path.isEmpty()) { + // If the blob still wasn't found then we'll prompt the user to download one + if (login_task_id_) { + model()->task_manager()->SetTaskFinished(login_task_id_); + } + + #ifdef Q_OS_LINUX + QMessageBox::StandardButton ret = QMessageBox::question(NULL, + tr("Spotify plugin not installed"), + tr("An additional plugin is required to use Spotify in Clementine. Would you like to download and install it now?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (ret == QMessageBox::Yes) { + // The downloader deletes itself when it finishes + SpotifyBlobDownloader* downloader = new SpotifyBlobDownloader( + local_blob_version_, local_blob_dir, this); + connect(downloader, SIGNAL(Finished()), SLOT(BlobDownloadFinished())); + downloader->Start(); + } + #endif + + return; + } + + delete blob_process_; + blob_process_ = new QProcess(this); + blob_process_->setProcessChannelMode(QProcess::ForwardedChannels); + + connect(blob_process_, + SIGNAL(error(QProcess::ProcessError)), + SLOT(BlobProcessError(QProcess::ProcessError))); + + qLog(Info) << "Starting" << blob_path; + blob_process_->start( + blob_path, QStringList() << QString::number(server_->server_port())); +} + +void SpotifyService::BlobDownloadFinished() { + EnsureServerCreated(); } void SpotifyService::PlaylistsUpdated(const protobuf::Playlists& response) { diff --git a/src/radio/spotifyservice.h b/src/radio/spotifyservice.h index 60a407d2c..f59355673 100644 --- a/src/radio/spotifyservice.h +++ b/src/radio/spotifyservice.h @@ -3,7 +3,7 @@ #include "radiomodel.h" #include "radioservice.h" -#include "spotifyblob/spotifymessages.pb.h" +#include "spotifyblob/common/spotifymessages.pb.h" #include #include @@ -37,6 +37,7 @@ public: static const char* kServiceName; static const char* kSettingsGroup; + static const char* kBlobDownloadUrl; static const int kSearchDelayMsec; QStandardItem* CreateRootItem(); @@ -61,6 +62,7 @@ protected: private: void EnsureServerCreated(const QString& username = QString(), const QString& password = QString()); + void StartBlobProcess(); void FillPlaylist(QStandardItem* item, const protobuf::LoadPlaylistResponse& response); void SongFromProtobuf(const protobuf::Track& track, Song* song) const; void EnsureMenuCreated(); @@ -82,12 +84,15 @@ private slots: void DoSearch(); void ShowConfig(); + void BlobDownloadFinished(); private: SpotifyServer* server_; SpotifyUrlHandler* url_handler_; - QString blob_path_; + QString local_blob_version_; + QString local_blob_path_; + QStringList blob_path_; QProcess* blob_process_; QStandardItem* root_;