#include "googledriveservice.h" #include #include #include #include "core/application.h" #include "core/closure.h" #include "core/database.h" #include "core/mergedproxymodel.h" #include "core/player.h" #include "core/timeconstants.h" #include "globalsearch/globalsearch.h" #include "globalsearch/librarysearchprovider.h" #include "library/librarybackend.h" #include "library/librarymodel.h" #include "googledriveclient.h" #include "googledriveurlhandler.h" #include "internetmodel.h" const char* GoogleDriveService::kServiceName = "Google Drive"; const char* GoogleDriveService::kSettingsGroup = "GoogleDrive"; namespace { static const char* kSongsTable = "google_drive_songs"; static const char* kFtsTable = "google_drive_songs_fts"; } GoogleDriveService::GoogleDriveService(Application* app, InternetModel* parent) : InternetService(kServiceName, app, parent, parent), root_(NULL), client_(new google_drive::Client(this)), library_sort_model_(new QSortFilterProxyModel(this)) { library_backend_ = new LibraryBackend; library_backend_->moveToThread(app_->database()->thread()); library_backend_->Init(app_->database(), kSongsTable, QString::null, QString::null, kFtsTable); library_model_ = new LibraryModel(library_backend_, app_, this); library_sort_model_->setSourceModel(library_model_); library_sort_model_->setSortRole(LibraryModel::Role_SortText); library_sort_model_->setDynamicSortFilter(true); library_sort_model_->sort(0); app->player()->RegisterUrlHandler(new GoogleDriveUrlHandler(this, this)); app->global_search()->AddProvider(new LibrarySearchProvider( library_backend_, tr("Google Drive"), "google_drive", QIcon(":/providers/googledrive.png"), true, app_, this)); } QStandardItem* GoogleDriveService::CreateRootItem() { root_ = new QStandardItem(QIcon(":providers/googledrive.png"), "Google Drive"); root_->setData(true, InternetModel::Role_CanLazyLoad); return root_; } void GoogleDriveService::LazyPopulate(QStandardItem* item) { switch (item->data(InternetModel::Role_Type).toInt()) { case InternetModel::Type_Service: Connect(); library_model_->Init(); model()->merged_model()->AddSubModel(item->index(), library_sort_model_); break; default: break; } } void GoogleDriveService::Connect() { QSettings s; s.beginGroup(kSettingsGroup); google_drive::ConnectResponse* response = client_->Connect(s.value("refresh_token").toString()); NewClosure(response, SIGNAL(Finished()), this, SLOT(ConnectFinished(google_drive::ConnectResponse*)), response); } void GoogleDriveService::ConnectFinished(google_drive::ConnectResponse* response) { response->deleteLater(); // Save the refresh token QSettings s; s.beginGroup(kSettingsGroup); s.setValue("refresh_token", response->refresh_token()); // Find any music files google_drive::ListFilesResponse* list_response = client_->ListFiles("mimeType = 'audio/mpeg'"); connect(list_response, SIGNAL(FilesFound(QList)), this, SLOT(FilesFound(QList))); NewClosure(list_response, SIGNAL(Finished()), this, SLOT(ListFilesFinished(google_drive::ListFilesResponse*))); } void GoogleDriveService::FilesFound(const QList& files) { foreach (const google_drive::File& file, files) { MaybeAddFileToDatabase(file); } } void GoogleDriveService::ListFilesFinished(google_drive::ListFilesResponse* response) { response->deleteLater(); } void GoogleDriveService::MaybeAddFileToDatabase(const google_drive::File& file) { QString url = QString("googledrive:%1").arg(file.id()); Song song = library_backend_->GetSongByUrl(QUrl(url)); // Song already in index. // TODO: Check etag and maybe update. if (song.is_valid()) { return; } // Song not in index; tag and add. TagReaderClient::ReplyType* reply = app_->tag_reader_client()->ReadGoogleDrive( file.download_url(), file.title(), file.size(), client_->access_token()); NewClosure(reply, SIGNAL(Finished(bool)), this, SLOT(ReadTagsFinished(TagReaderClient::ReplyType*,google_drive::File,QString)), reply, file, url); } void GoogleDriveService::ReadTagsFinished(TagReaderClient::ReplyType* reply, const google_drive::File& metadata, const QString& url) { reply->deleteLater(); const pb::tagreader::ReadGoogleDriveResponse& msg = reply->message().read_google_drive_response(); if (!msg.metadata().filesize()) { qLog(Debug) << "Failed to tag:" << metadata.download_url(); return; } // Read the Song metadata from the message. Song song; song.InitFromProtobuf(msg.metadata()); // Add some extra tags from the Google Drive metadata. song.set_etag(metadata.etag().remove('"')); song.set_mtime(metadata.modified_date().toTime_t()); song.set_ctime(metadata.created_date().toTime_t()); song.set_comment(metadata.description()); song.set_directory_id(0); song.set_url(url); // Use the Google Drive title if we couldn't read tags from the file. if (song.title().isEmpty()) { song.set_title(metadata.title()); } // Add the song to the database qLog(Debug) << "Adding song to db:" << song.title(); library_backend_->AddOrUpdateSongs(SongList() << song); } QUrl GoogleDriveService::GetStreamingUrlFromSongId(const QString& id) { QScopedPointer response(client_->GetFile(id)); QEventLoop loop; connect(response.data(), SIGNAL(Finished()), &loop, SLOT(quit())); loop.exec(); QUrl url(response->file().download_url()); url.setFragment(client_->access_token()); return url; }