Merge pull request #4456 from Shedward/vk-broadcasting

Vk.com broadcasting.
This commit is contained in:
John Maguire 2014-08-12 22:04:45 +02:00
commit e876df1b23
13 changed files with 279 additions and 177 deletions

View File

@ -210,6 +210,29 @@ IntReply *AudioProvider::removeFromLibrary(int aid, int oid)
return reply; return reply;
} }
IdListReply *AudioProvider::setBroadcast(int aid, int oid, const IdList &targetIds)
{
Q_D(AudioProvider);
QVariantMap args;
args.insert("audio", QString("%1_%2").arg(oid).arg(aid));
args.insert("target_ids", join(targetIds));
auto reply = d->client->request<IdListReply>("audio.setBroadcast", args, ReplyPrivate::handleIdList);
return reply;
}
IdListReply *AudioProvider::resetBroadcast(const IdList &targetIds)
{
Q_D(AudioProvider);
QVariantMap args;
args.insert("audio","");
args.insert("target_ids", join(targetIds));
auto reply = d->client->request<IdListReply>("audio.setBroadcast", args, ReplyPrivate::handleIdList);
return reply;
}
AudioItemListReply *AudioProvider::getAudiosByIds(const QString &ids) AudioItemListReply *AudioProvider::getAudiosByIds(const QString &ids)
{ {
Q_D(AudioProvider); Q_D(AudioProvider);

View File

@ -26,6 +26,7 @@
#define VK_AUDIO_H #define VK_AUDIO_H
#include <QAbstractListModel> #include <QAbstractListModel>
#include "vk_global.h"
#include "audioitem.h" #include "audioitem.h"
#include "abstractlistmodel.h" #include "abstractlistmodel.h"
#include "reply.h" #include "reply.h"
@ -35,6 +36,7 @@ namespace Vreen {
class Client; class Client;
typedef ReplyBase<AudioItemList> AudioItemListReply; typedef ReplyBase<AudioItemList> AudioItemListReply;
typedef ReplyBase<AudioAlbumItemList> AudioAlbumItemListReply; typedef ReplyBase<AudioAlbumItemList> AudioAlbumItemListReply;
typedef ReplyBase<QList<int>> IdListReply;
class AudioProviderPrivate; class AudioProviderPrivate;
class VK_SHARED_EXPORT AudioProvider : public QObject class VK_SHARED_EXPORT AudioProvider : public QObject
@ -60,6 +62,8 @@ public:
IntReply *getCount(int oid = 0); IntReply *getCount(int oid = 0);
IntReply *addToLibrary(int aid, int oid, int gid = 0); IntReply *addToLibrary(int aid, int oid, int gid = 0);
IntReply *removeFromLibrary(int aid, int oid); IntReply *removeFromLibrary(int aid, int oid);
IdListReply *setBroadcast(int aid, int oid, const IdList& targetIds);
IdListReply *resetBroadcast(const IdList& targetIds);
protected: protected:
QScopedPointer<AudioProviderPrivate> d_ptr; QScopedPointer<AudioProviderPrivate> d_ptr;
}; };

View File

@ -122,6 +122,16 @@ void ReplyPrivate::_q_network_reply_error(QNetworkReply::NetworkError code)
emit q->resultReady(response); emit q->resultReady(response);
} }
QVariant ReplyPrivate::handleIdList(const QVariant &response)
{
IdList ids;
auto list = response.toList();
foreach (auto item, list) {
ids.append(item.toInt());
}
return QVariant::fromValue(ids);
}
QVariant MessageListHandler::operator()(const QVariant &response) QVariant MessageListHandler::operator()(const QVariant &response)
{ {

View File

@ -51,6 +51,7 @@ public:
void _q_network_reply_error(QNetworkReply::NetworkError); void _q_network_reply_error(QNetworkReply::NetworkError);
static QVariant handleInt(const QVariant &response) { return response.toInt(); } static QVariant handleInt(const QVariant &response) { return response.toInt(); }
static QVariant handleIdList(const QVariant& response);
}; };

View File

@ -30,17 +30,16 @@
static const QUrl kVkOAuthEndpoint("https://oauth.vk.com/authorize"); static const QUrl kVkOAuthEndpoint("https://oauth.vk.com/authorize");
static const QUrl kVkOAuthTokenEndpoint("https://oauth.vk.com/access_token"); static const QUrl kVkOAuthTokenEndpoint("https://oauth.vk.com/access_token");
static const QUrl kApiUrl("https://api.vk.com/method/"); static const QUrl kApiUrl("https://api.vk.com/method/");
static const char *kScopeNames[] = { "notify", "friends", "photos", "audio", static const char* kScopeNames[] = {
"video", "docs", "notes", "pages", "status", "offers", "questions", "wall", "notify", "friends", "photos", "audio", "video", "docs",
"notes", "pages", "status", "offers", "questions", "wall",
"groups", "messages", "notifications", "stats", "ads", "offline"}; "groups", "messages", "notifications", "stats", "ads", "offline"};
static const QString kAppID = "3421812"; static const QString kAppID = "3421812";
static const QString kAppSecret = "cY7KMyX46Fq3nscZlbdo"; static const QString kAppSecret = "cY7KMyX46Fq3nscZlbdo";
static const VkConnection::Scopes kScopes = static const VkConnection::Scopes kScopes =
VkConnection::Offline | VkConnection::Offline | VkConnection::Audio | VkConnection::Friends |
VkConnection::Audio | VkConnection::Groups | VkConnection::Status;
VkConnection::Friends |
VkConnection::Groups;
static const char* kSettingsGroup = "Vk.com/oauth"; static const char* kSettingsGroup = "Vk.com/oauth";
@ -52,10 +51,10 @@ VkConnection::VkConnection(QObject* parent)
loadToken(); loadToken();
} }
VkConnection::~VkConnection() { VkConnection::~VkConnection() {}
}
void VkConnection::connectToHost(const QString& login, const QString& password) { void VkConnection::connectToHost(const QString& login,
const QString& password) {
Q_UNUSED(login) Q_UNUSED(login)
Q_UNUSED(password) Q_UNUSED(password)
if (hasAccount()) { if (hasAccount()) {
@ -84,16 +83,17 @@ void VkConnection::clear() {
} }
bool VkConnection::hasAccount() { bool VkConnection::hasAccount() {
return !access_token_.isNull() return !access_token_.isNull() &&
&& (expires_in_ > static_cast<time_t>(QDateTime::currentDateTime().toTime_t())); (expires_in_ >
static_cast<time_t>(QDateTime::currentDateTime().toTime_t()));
} }
QNetworkRequest VkConnection::makeRequest(const QString& method, const QVariantMap& args) { QNetworkRequest VkConnection::makeRequest(const QString& method,
const QVariantMap& args) {
QUrl url = kApiUrl; QUrl url = kApiUrl;
url.setPath(url.path() % QLatin1Literal("/") % method); url.setPath(url.path() % QLatin1Literal("/") % method);
for (auto it = args.constBegin(); it != args.constEnd(); ++it) { for (auto it = args.constBegin(); it != args.constEnd(); ++it) {
url.addQueryItem(it.key(), url.addQueryItem(it.key(), it.value().toString());
it.value().toString());
} }
url.addEncodedQueryItem("access_token", access_token_); url.addEncodedQueryItem("access_token", access_token_);
return QNetworkRequest(url); return QNetworkRequest(url);
@ -118,9 +118,9 @@ void VkConnection::requestAccessToken() {
qLog(Debug) << "Try to login to Vk.com" << url; qLog(Debug) << "Try to login to Vk.com" << url;
NewClosure(server, SIGNAL(Finished()), NewClosure(server, SIGNAL(Finished()), this,
this, SLOT(codeRecived(LocalRedirectServer*, QUrl)), SLOT(codeRecived(LocalRedirectServer*, QUrl)), server,
server, server->url()); server->url());
QDesktopServices::openUrl(url); QDesktopServices::openUrl(url);
} }

View File

@ -28,41 +28,37 @@ VkMusicCache::VkMusicCache(Application* app, VkService* service)
: QObject(service), : QObject(service),
app_(app), app_(app),
service_(service), service_(service),
current_cashing_index(0), current_song_index(0),
is_downloading(false), is_downloading(false),
is_aborted(false), is_aborted(false),
task_id(0), task_id(0),
file_(NULL), file_(NULL),
network_manager_(new QNetworkAccessManager), network_manager_(new QNetworkAccessManager),
reply_(NULL) { reply_(NULL) {}
}
QUrl VkMusicCache::Get(const QUrl& url) { QUrl VkMusicCache::Get(const QUrl& url) {
QString cached_filename = CachedFilename(url);
QUrl result; QUrl result;
if (InCache(cached_filename)) { if (InCache(url)) {
QString cached_filename = CachedFilename(url);
qLog(Info) << "Use cashed file" << cached_filename; qLog(Info) << "Use cashed file" << cached_filename;
result = QUrl::fromLocalFile(cached_filename); result = QUrl::fromLocalFile(cached_filename);
} else {
result = service_->GetSongPlayUrl(url, false);
if (service_->isCachingEnabled()) {
AddToQueue(cached_filename, result);
current_cashing_index = queue_.size();
}
} }
return result; return result;
} }
void VkMusicCache::ForceCache(const QUrl& url) { void VkMusicCache::AddToCache(const QUrl& url, const QUrl& media_url,
AddToQueue(CachedFilename(url), service_->GetSongPlayUrl(url)); bool force) {
AddToQueue(CachedFilename(url), media_url);
if (!force) {
current_song_index = queue_.size();
}
} }
void VkMusicCache::BreakCurrentCaching() { void VkMusicCache::BreakCurrentCaching() {
if (current_cashing_index > 0) { if (current_song_index > 0) {
// Current song in queue // Current song in queue
queue_.removeAt(current_cashing_index - 1); queue_.removeAt(current_song_index - 1);
} else if (current_cashing_index == 0) { } else if (current_song_index == 0) {
// Current song is downloading // Current song is downloading
if (reply_) { if (reply_) {
reply_->abort(); reply_->abort();
@ -75,7 +71,8 @@ void VkMusicCache::BreakCurrentCaching() {
* Queue operations * Queue operations
*/ */
void VkMusicCache::AddToQueue(const QString& filename, const QUrl& download_url) { void VkMusicCache::AddToQueue(const QString& filename,
const QUrl& download_url) {
DownloadItem item; DownloadItem item;
item.filename = filename; item.filename = filename;
item.url = download_url; item.url = download_url;
@ -93,11 +90,12 @@ void VkMusicCache::DownloadNext() {
} else { } else {
current_download = queue_.first(); current_download = queue_.first();
queue_.pop_front(); queue_.pop_front();
current_cashing_index--; current_song_index--;
// Check file path and file existance first // Check file path and file existance first
if (QFile::exists(current_download.filename)) { if (QFile::exists(current_download.filename)) {
qLog(Warning) << "Tried to overwrite already cached file" << current_download.filename; qLog(Warning) << "Tried to overwrite already cached file"
<< current_download.filename;
return; return;
} }
@ -117,14 +115,15 @@ void VkMusicCache::DownloadNext() {
// Start downloading // Start downloading
is_aborted = false; is_aborted = false;
is_downloading = true; is_downloading = true;
task_id = app_->task_manager()-> task_id = app_->task_manager()->StartTask(
StartTask(tr("Caching %1") tr("Caching %1").arg(QFileInfo(current_download.filename).baseName()));
.arg(QFileInfo(current_download.filename).baseName()));
reply_ = network_manager_->get(QNetworkRequest(current_download.url)); reply_ = network_manager_->get(QNetworkRequest(current_download.url));
connect(reply_, SIGNAL(finished()), SLOT(Downloaded())); connect(reply_, SIGNAL(finished()), SLOT(Downloaded()));
connect(reply_, SIGNAL(readyRead()), SLOT(DownloadReadyToRead())); connect(reply_, SIGNAL(readyRead()), SLOT(DownloadReadyToRead()));
connect(reply_, SIGNAL(downloadProgress(qint64, qint64)), SLOT(DownloadProgress(qint64, qint64))); connect(reply_, SIGNAL(downloadProgress(qint64, qint64)),
qLog(Info)<< "Start cashing" << current_download.filename << "from" << current_download.url; SLOT(DownloadProgress(qint64, qint64)));
qLog(Info) << "Start cashing" << current_download.filename << "from"
<< current_download.url;
} }
} }
@ -159,8 +158,8 @@ void VkMusicCache::Downloaded() {
if (file_->copy(current_download.filename)) { if (file_->copy(current_download.filename)) {
qLog(Info) << "Cached" << current_download.filename; qLog(Info) << "Cached" << current_download.filename;
} else { } else {
qLog(Error) << "Unable to save" << current_download.filename qLog(Error) << "Unable to save" << current_download.filename << ":"
<< ":" << file_->errorString(); << file_->errorString();
} }
} else { } else {
qLog(Error) << "File" << current_download.filename << "is empty"; qLog(Error) << "File" << current_download.filename << "is empty";
@ -181,12 +180,8 @@ void VkMusicCache::Downloaded() {
* Utils * Utils
*/ */
bool VkMusicCache::InCache(const QString& filename) {
return QFile::exists(filename);
}
bool VkMusicCache::InCache(const QUrl& url) { bool VkMusicCache::InCache(const QUrl& url) {
return InCache(CachedFilename(url)); return QFile::exists(CachedFilename(url));
} }
QString VkMusicCache::CachedFilename(const QUrl& url) { QString VkMusicCache::CachedFilename(const QUrl& url) {
@ -198,7 +193,8 @@ QString VkMusicCache::CachedFilename(const QUrl& url) {
cache_filename.replace("%artist", args[2]); cache_filename.replace("%artist", args[2]);
cache_filename.replace("%title", args[3]); cache_filename.replace("%title", args[3]);
} else { } else {
qLog(Warning) << "Song url with args" << args << "does not contain artist and title" qLog(Warning) << "Song url with args" << args
<< "does not contain artist and title"
<< "use id as file name for cache."; << "use id as file name for cache.";
cache_filename = args[1]; cache_filename = args[1];
} }

View File

@ -36,12 +36,11 @@ public:
// Return file path if file in cache otherwise // Return file path if file in cache otherwise
// return internet url and add song to caching queue // return internet url and add song to caching queue
QUrl Get(const QUrl& url); QUrl Get(const QUrl& url);
void ForceCache(const QUrl& url); void AddToCache(const QUrl& url, const QUrl& media_url, bool force = false);
void BreakCurrentCaching(); void BreakCurrentCaching();
bool InCache(const QUrl& url); bool InCache(const QUrl& url);
private slots: private slots:
bool InCache(const QString& filename);
void AddToQueue(const QString& filename, const QUrl& download_url); void AddToQueue(const QString& filename, const QUrl& download_url);
void DownloadNext(); void DownloadNext();
void DownloadProgress(qint64 bytesReceived, qint64 bytesTotal); void DownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
@ -63,9 +62,10 @@ private:
Application* app_; Application* app_;
VkService* service_; VkService* service_;
QList<DownloadItem> queue_; QList<DownloadItem> queue_;
// Contain index of current song in queue, need for removing if song was skipped. // Contain index of current song in queue, need for removing if song was
// Is zero if song downloading now, and less that zero if current song not caching or cached. // skipped. It's zero if song downloading now, and less that zero
int current_cashing_index; // if current song not caching or cached.
int current_song_index;
DownloadItem current_download; DownloadItem current_download;
bool is_downloading; bool is_downloading;
bool is_aborted; bool is_aborted;

View File

@ -312,7 +312,7 @@ void VkService::EnsureMenuCreated() {
add_song_to_cache_ = context_menu_->addAction(QIcon(":vk/download.png"), add_song_to_cache_ = context_menu_->addAction(QIcon(":vk/download.png"),
tr("Add song to cache"), this, tr("Add song to cache"), this,
SLOT(AddToCache())); SLOT(AddSelectedToCache()));
copy_share_url_ = context_menu_->addAction( copy_share_url_ = context_menu_->addAction(
QIcon(":vk/link.png"), tr("Copy share url to clipboard"), this, QIcon(":vk/link.png"), tr("Copy share url to clipboard"), this,
@ -367,7 +367,7 @@ void VkService::ShowContextMenu(const QPoint& global_pos) {
current.data(InternetModel::Role_SongMetadata).value<Song>(); current.data(InternetModel::Role_SongMetadata).value<Song>();
is_in_mymusic = is_my_music_item || is_in_mymusic = is_my_music_item ||
ExtractIds(selected_song_.url()).owner_id == UserID(); ExtractIds(selected_song_.url()).owner_id == UserID();
is_cached = cache()->InCache(selected_song_.url()); is_cached = cache_->InCache(selected_song_.url());
} }
update_item_->setVisible(is_updatable); update_item_->setVisible(is_updatable);
@ -443,7 +443,7 @@ QList<QAction*> VkService::playlistitem_actions(const Song& song) {
copy_share_url_->setVisible(true); copy_share_url_->setVisible(true);
actions << copy_share_url_; actions << copy_share_url_;
if (!cache()->InCache(selected_song_.url())) { if (!cache_->InCache(selected_song_.url())) {
add_song_to_cache_->setVisible(true); add_song_to_cache_->setVisible(true);
actions << add_song_to_cache_; actions << add_song_to_cache_;
} }
@ -835,7 +835,7 @@ void VkService::AddToMyMusic() {
} }
void VkService::AddToMyMusicCurrent() { void VkService::AddToMyMusicCurrent() {
if (isLoveAddToMyMusic()) { if (isLoveAddToMyMusic() && current_song_.is_valid()) {
selected_song_ = current_song_; selected_song_ = current_song_;
AddToMyMusic(); AddToMyMusic();
} }
@ -852,8 +852,10 @@ void VkService::RemoveFromMyMusic() {
} }
} }
void VkService::AddToCache() { void VkService::AddSelectedToCache() {
url_handler_->ForceAddToCache(selected_song_.url()); QUrl selected_song_media_url =
GetAudioItemFromUrl(selected_song_.url()).url();
cache_->AddToCache(selected_song_.url(), selected_song_media_url, true);
} }
void VkService::CopyShareUrl() { void VkService::CopyShareUrl() {
@ -999,12 +1001,12 @@ SongList VkService::FromAudioList(const Vreen::AudioItemList& list) {
* Url handling * Url handling
*/ */
QUrl VkService::GetSongPlayUrl(const QUrl& url, bool is_playing) { Vreen::AudioItem VkService::GetAudioItemFromUrl(const QUrl& url) {
QStringList tokens = url.path().split('/'); QStringList tokens = url.path().split('/');
if (tokens.count() < 2) { if (tokens.count() < 2) {
qLog(Error) << "Wrong song url" << url; qLog(Error) << "Wrong song url" << url;
return QUrl(); return Vreen::AudioItem();
} }
QString song_id = tokens[1]; QString song_id = tokens[1];
@ -1016,17 +1018,35 @@ QUrl VkService::GetSongPlayUrl(const QUrl& url, bool is_playing) {
bool success = WaitForReply(song_request); bool success = WaitForReply(song_request);
if (success && !song_request->result().isEmpty()) { if (success && !song_request->result().isEmpty()) {
Vreen::AudioItem song = song_request->result()[0]; return song_request->result()[0];
if (is_playing) {
current_song_ = FromAudioItem(song);
current_song_.set_url(url);
}
return song.url();
} }
} }
qLog(Info) << "Unresolved url by id" << song_id; qLog(Info) << "Unresolved url by id" << song_id;
return QUrl(); return Vreen::AudioItem();
}
UrlHandler::LoadResult VkService::GetSongResult(const QUrl& url) {
// Try get from cache
QUrl media_url = cache_->Get(url);
if (media_url.isValid()) {
SongStarting(url);
return UrlHandler::LoadResult(url, UrlHandler::LoadResult::TrackAvailable,
media_url);
}
// Otherwise get fresh link
auto audio_item = GetAudioItemFromUrl(url);
media_url = audio_item.url();
if (media_url.isValid()) {
Song song = FromAudioItem(audio_item);
SongStarting(song);
cache_->AddToCache(url, media_url);
return UrlHandler::LoadResult(url, UrlHandler::LoadResult::TrackAvailable,
media_url, song.length_nanosec());
}
return UrlHandler::LoadResult();
} }
UrlHandler::LoadResult VkService::GetGroupNextSongUrl(const QUrl& url) { UrlHandler::LoadResult VkService::GetGroupNextSongUrl(const QUrl& url) {
@ -1054,7 +1074,7 @@ UrlHandler::LoadResult VkService::GetGroupNextSongUrl(const QUrl& url) {
if (success && !song_request->result().isEmpty()) { if (success && !song_request->result().isEmpty()) {
Vreen::AudioItem song = song_request->result()[0]; Vreen::AudioItem song = song_request->result()[0];
current_group_url_ = url; current_group_url_ = url;
current_song_ = FromAudioItem(song); SongStarting(FromAudioItem(song));
emit StreamMetadataFound(url, current_song_); emit StreamMetadataFound(url, current_song_);
return UrlHandler::LoadResult(url, UrlHandler::LoadResult::TrackAvailable, return UrlHandler::LoadResult(url, UrlHandler::LoadResult::TrackAvailable,
song.url(), current_song_.length_nanosec()); song.url(), current_song_.length_nanosec());
@ -1065,8 +1085,48 @@ UrlHandler::LoadResult VkService::GetGroupNextSongUrl(const QUrl& url) {
return UrlHandler::LoadResult(); return UrlHandler::LoadResult();
} }
void VkService::SetCurrentSongFromUrl(const QUrl& url) { /***
current_song_ = SongFromUrl(url); * Song playing
*/
void VkService::SongStarting(const QUrl& url) {
SongStarting(SongFromUrl(url));
}
void VkService::SongStarting(const Song& song) {
current_song_ = song;
if (isBroadcasting() && HasAccount()) {
auto id = ExtractIds(song.url());
auto reply =
audio_provider_->setBroadcast(id.audio_id, id.owner_id, IdList());
NewClosure(reply, SIGNAL(resultReady(QVariant)), this,
SLOT(BroadcastChangeReceived(Vreen::IntReply*)), reply);
connect(app_->player(), SIGNAL(Stopped()), this, SLOT(SongStopped()),
Qt::UniqueConnection);
qLog(Debug) << "Broadcasting" << song.artist() << "-" << song.title();
}
}
void VkService::SongSkipped() {
current_song_.set_valid(false);
cache_->BreakCurrentCaching();
}
void VkService::SongStopped() {
current_song_.set_valid(false);
if (isBroadcasting() && HasAccount()) {
auto reply = audio_provider_->resetBroadcast(IdList());
NewClosure(reply, SIGNAL(resultReady(QVariant)), this,
SLOT(BroadcastChangeReceived(Vreen::IntReply*)), reply);
disconnect(app_->player(), SIGNAL(Stopped()), this, SLOT(SongStopped()));
qLog(Debug) << "End of broadcasting";
}
}
void VkService::BroadcastChangeReceived(Vreen::IntReply* reply) {
qLog(Debug) << "Broadcast changed for " << reply->result();
} }
/*** /***
@ -1234,6 +1294,12 @@ void VkService::ReloadSettings() {
cacheFilename_ = s.value("cache_filename", kDefCacheFilename).toString(); cacheFilename_ = s.value("cache_filename", kDefCacheFilename).toString();
love_is_add_to_mymusic_ = s.value("love_is_add_to_my_music", false).toBool(); love_is_add_to_mymusic_ = s.value("love_is_add_to_my_music", false).toBool();
groups_in_global_search_ = s.value("groups_in_global_search", false).toBool(); groups_in_global_search_ = s.value("groups_in_global_search", false).toBool();
if (!s.contains("enable_broadcast")) {
// Need to update premissions
Logout();
}
enable_broadcast_ = s.value("enable_broadcast", false).toBool();
} }
void VkService::ClearStandardItem(QStandardItem* item) { void VkService::ClearStandardItem(QStandardItem* item) {

View File

@ -45,10 +45,7 @@ class VkSearchDialog;
*/ */
class MusicOwner { class MusicOwner {
public: public:
MusicOwner() : MusicOwner() : songs_count_(0), id_(0) {}
songs_count_(0),
id_(0)
{}
explicit MusicOwner(const QUrl& group_url); explicit MusicOwner(const QUrl& group_url);
Song toOwnerRadio() const; Song toOwnerRadio() const;
@ -66,7 +63,8 @@ private:
int songs_count_; int songs_count_;
int id_; // if id > 0 is user otherwise id group int id_; // if id > 0 is user otherwise id group
QString name_; QString name_;
// name used in url http://vk.com/<screen_name> for example: http://vk.com/shedward // name used in url http://vk.com/<screen_name> for example:
// http://vk.com/shedward
QString screen_name_; QString screen_name_;
QUrl photo_; QUrl photo_;
}; };
@ -84,19 +82,12 @@ QDebug operator<<(QDebug d, const MusicOwner& owner);
* how to react to the received request or quickly skip unwanted. * how to react to the received request or quickly skip unwanted.
*/ */
struct SearchID { struct SearchID {
enum Type { enum Type { GlobalSearch, LocalSearch, MoreLocalSearch, UserOrGroup };
GlobalSearch,
LocalSearch,
MoreLocalSearch,
UserOrGroup
};
explicit SearchID(Type type) explicit SearchID(Type type) : type_(type) { id_ = last_id_++; }
: type_(type) {
id_= last_id_++;
}
int id() const { return id_; } int id() const { return id_; }
Type type() const { return type_; } Type type() const { return type_; }
private: private:
static uint last_id_; static uint last_id_;
int id_; int id_;
@ -133,8 +124,12 @@ public:
Type_Search Type_Search
}; };
enum Role { Role_MusicOwnerMetadata = InternetModel::RoleCount, enum Role {
Role_AlbumMetadata }; Role_MusicOwnerMetadata = InternetModel::RoleCount,
Role_AlbumMetadata
};
Application* app() const { return app_; }
/* InternetService interface */ /* InternetService interface */
QStandardItem* CreateRootItem(); QStandardItem* CreateRootItem();
@ -155,13 +150,16 @@ public:
bool WaitForReply(Vreen::Reply* reply); bool WaitForReply(Vreen::Reply* reply);
/* Music */ /* Music */
VkMusicCache* cache() const { return cache_; } void SongStarting(const Song& song);
void SetCurrentSongFromUrl(const QUrl& url); // Used if song taked from cache. void SongStarting(const QUrl& url); // Used if song taked from cache.
QUrl GetSongPlayUrl(const QUrl& url, bool is_playing = true); void SongSkipped();
UrlHandler::LoadResult GetSongResult(const QUrl& url);
Vreen::AudioItem GetAudioItemFromUrl(const QUrl& url);
// Return random song result from group playlist. // Return random song result from group playlist.
UrlHandler::LoadResult GetGroupNextSongUrl(const QUrl& url); UrlHandler::LoadResult GetGroupNextSongUrl(const QUrl& url);
void SongSearch(SearchID id, const QString& query, int count = 50, int offset = 0); void SongSearch(SearchID id, const QString& query, int count = 50,
int offset = 0);
void GroupSearch(SearchID id, const QString& query); void GroupSearch(SearchID id, const QString& query);
/* Settings */ /* Settings */
@ -169,6 +167,7 @@ public:
int maxGlobalSearch() const { return maxGlobalSearch_; } int maxGlobalSearch() const { return maxGlobalSearch_; }
bool isCachingEnabled() const { return cachingEnabled_; } bool isCachingEnabled() const { return cachingEnabled_; }
bool isGroupsInGlobalSearch() const { return groups_in_global_search_; } bool isGroupsInGlobalSearch() const { return groups_in_global_search_; }
bool isBroadcasting() const { return enable_broadcast_; }
QString cacheDir() const { return cacheDir_; } QString cacheDir() const { return cacheDir_; }
QString cacheFilename() const { return cacheFilename_; } QString cacheFilename() const { return cacheFilename_; }
bool isLoveAddToMyMusic() const { return love_is_add_to_mymusic_; } bool isLoveAddToMyMusic() const { return love_is_add_to_mymusic_; }
@ -179,7 +178,8 @@ signals:
void LoginSuccess(bool success); void LoginSuccess(bool success);
void SongSearchResult(const SearchID& id, const SongList& songs); void SongSearchResult(const SearchID& id, const SongList& songs);
void GroupSearchResult(const SearchID& id, const MusicOwnerList& groups); void GroupSearchResult(const SearchID& id, const MusicOwnerList& groups);
void UserOrGroupSearchResult(const SearchID& id, const MusicOwnerList& owners); void UserOrGroupSearchResult(const SearchID& id,
const MusicOwnerList& owners);
void StopWaiting(); void StopWaiting();
public slots: public slots:
@ -197,6 +197,7 @@ private slots:
void Error(Vreen::Client::Error error); void Error(Vreen::Client::Error error);
/* Music */ /* Music */
void SongStopped();
void UpdateMyMusic(); void UpdateMyMusic();
void UpdateBookmarkSongs(QStandardItem* item); void UpdateBookmarkSongs(QStandardItem* item);
void UpdateAlbumSongs(QStandardItem* item); void UpdateAlbumSongs(QStandardItem* item);
@ -208,7 +209,7 @@ private slots:
void AddToMyMusic(); void AddToMyMusic();
void AddToMyMusicCurrent(); void AddToMyMusicCurrent();
void RemoveFromMyMusic(); void RemoveFromMyMusic();
void AddToCache(); void AddSelectedToCache();
void CopyShareUrl(); void CopyShareUrl();
void ShowSearchDialog(); void ShowSearchDialog();
@ -219,6 +220,7 @@ private slots:
void GroupSearchReceived(const SearchID& id, Vreen::Reply* reply); void GroupSearchReceived(const SearchID& id, Vreen::Reply* reply);
void UserOrGroupReceived(const SearchID& id, Vreen::Reply* reply); void UserOrGroupReceived(const SearchID& id, Vreen::Reply* reply);
void AlbumListReceived(Vreen::AudioAlbumItemListReply* reply); void AlbumListReceived(Vreen::AudioAlbumItemListReply* reply);
void BroadcastChangeReceived(Vreen::IntReply* reply);
void AppendLoadedSongs(QStandardItem* item, Vreen::AudioItemListReply* reply); void AppendLoadedSongs(QStandardItem* item, Vreen::AudioItemListReply* reply);
void RecommendationsLoaded(Vreen::AudioItemListReply* reply); void RecommendationsLoaded(Vreen::AudioItemListReply* reply);
@ -226,7 +228,8 @@ private slots:
private: private:
/* Interface */ /* Interface */
QStandardItem* CreateAndAppendRow(QStandardItem* parent, VkService::ItemType type); QStandardItem* CreateAndAppendRow(QStandardItem* parent,
VkService::ItemType type);
void ClearStandardItem(QStandardItem* item); void ClearStandardItem(QStandardItem* item);
QStandardItem* GetBookmarkItemById(int id); QStandardItem* GetBookmarkItemById(int id);
void EnsureMenuCreated(); void EnsureMenuCreated();
@ -288,6 +291,7 @@ private:
bool cachingEnabled_; bool cachingEnabled_;
bool love_is_add_to_mymusic_; bool love_is_add_to_mymusic_;
bool groups_in_global_search_; bool groups_in_global_search_;
bool enable_broadcast_;
QString cacheDir_; QString cacheDir_;
QString cacheFilename_; QString cacheFilename_;
}; };

View File

@ -30,27 +30,24 @@ VkSettingsPage::VkSettingsPage(SettingsDialog *parent)
ui_(new Ui::VkSettingsPage), ui_(new Ui::VkSettingsPage),
service_(dialog()->app()->internet_model()->Service<VkService>()) { service_(dialog()->app()->internet_model()->Service<VkService>()) {
ui_->setupUi(this); ui_->setupUi(this);
connect(service_, SIGNAL(LoginSuccess(bool)), connect(service_, SIGNAL(LoginSuccess(bool)), SLOT(LoginSuccess(bool)));
SLOT(LoginSuccess(bool))); connect(ui_->choose_path, SIGNAL(clicked()), SLOT(CacheDirBrowse()));
connect(ui_->choose_path, SIGNAL(clicked()), connect(ui_->reset, SIGNAL(clicked()), SLOT(ResetCasheFilenames()));
SLOT(CacheDirBrowse()));
connect(ui_->reset, SIGNAL(clicked()),
SLOT(ResetCasheFilenames()));
} }
VkSettingsPage::~VkSettingsPage() { VkSettingsPage::~VkSettingsPage() { delete ui_; }
delete ui_;
}
void VkSettingsPage::Load() { void VkSettingsPage::Load() {
service_->ReloadSettings(); service_->ReloadSettings();
ui_->maxGlobalSearch->setValue(service_->maxGlobalSearch()); ui_->max_global_search->setValue(service_->maxGlobalSearch());
ui_->enable_caching->setChecked(service_->isCachingEnabled()); ui_->enable_caching->setChecked(service_->isCachingEnabled());
ui_->cache_dir->setText(service_->cacheDir()); ui_->cache_dir->setText(service_->cacheDir());
ui_->cache_filename->setText(service_->cacheFilename()); ui_->cache_filename->setText(service_->cacheFilename());
ui_->love_button_is_add_to_mymusic->setChecked(service_->isLoveAddToMyMusic()); ui_->love_button_is_add_to_mymusic->setChecked(
service_->isLoveAddToMyMusic());
ui_->groups_in_global_search->setChecked(service_->isGroupsInGlobalSearch()); ui_->groups_in_global_search->setChecked(service_->isGroupsInGlobalSearch());
ui_->enable_broadcast->setChecked(service_->isBroadcasting());
if (service_->HasAccount()) { if (service_->HasAccount()) {
LogoutWidgets(); LogoutWidgets();
@ -63,12 +60,15 @@ void VkSettingsPage::Save() {
QSettings s; QSettings s;
s.beginGroup(VkService::kSettingGroup); s.beginGroup(VkService::kSettingGroup);
s.setValue("max_global_search", ui_->maxGlobalSearch->value()); s.setValue("max_global_search", ui_->max_global_search->value());
s.setValue("cache_enabled", ui_->enable_caching->isChecked()); s.setValue("cache_enabled", ui_->enable_caching->isChecked());
s.setValue("cache_dir", ui_->cache_dir->text()); s.setValue("cache_dir", ui_->cache_dir->text());
s.setValue("cache_filename", ui_->cache_filename->text()); s.setValue("cache_filename", ui_->cache_filename->text());
s.setValue("love_is_add_to_my_music", ui_->love_button_is_add_to_mymusic->isChecked()); s.setValue("love_is_add_to_my_music",
s.setValue("groups_in_global_search", ui_->groups_in_global_search->isChecked()); ui_->love_button_is_add_to_mymusic->isChecked());
s.setValue("groups_in_global_search",
ui_->groups_in_global_search->isChecked());
s.setValue("enable_broadcast", ui_->enable_broadcast->isChecked());
service_->ReloadSettings(); service_->ReloadSettings();
} }
@ -111,10 +111,9 @@ void VkSettingsPage::LoginWidgets() {
ui_->name->setText(""); ui_->name->setText("");
ui_->login_button->setEnabled(true); ui_->login_button->setEnabled(true);
connect(ui_->login_button, SIGNAL(clicked()), connect(ui_->login_button, SIGNAL(clicked()), SLOT(Login()),
SLOT(Login()), Qt::UniqueConnection); Qt::UniqueConnection);
disconnect(ui_->login_button, SIGNAL(clicked()), disconnect(ui_->login_button, SIGNAL(clicked()), this, SLOT(Logout()));
this, SLOT(Logout()));
} }
void VkSettingsPage::LogoutWidgets() { void VkSettingsPage::LogoutWidgets() {
@ -122,12 +121,11 @@ void VkSettingsPage::LogoutWidgets() {
ui_->name->setText(tr("Loading...")); ui_->name->setText(tr("Loading..."));
ui_->login_button->setEnabled(true); ui_->login_button->setEnabled(true);
connect(service_, SIGNAL(NameUpdated(QString)), connect(service_, SIGNAL(NameUpdated(QString)), ui_->name,
ui_->name, SLOT(setText(QString)), Qt::UniqueConnection); SLOT(setText(QString)), Qt::UniqueConnection);
service_->RequestUserProfile(); service_->RequestUserProfile();
connect(ui_->login_button, SIGNAL(clicked()), connect(ui_->login_button, SIGNAL(clicked()), SLOT(Logout()),
SLOT(Logout()), Qt::UniqueConnection); Qt::UniqueConnection);
disconnect(ui_->login_button, SIGNAL(clicked()), disconnect(ui_->login_button, SIGNAL(clicked()), this, SLOT(Login()));
this, SLOT(Login()));
} }

View File

@ -64,12 +64,12 @@
<string>Max global search results</string> <string>Max global search results</string>
</property> </property>
<property name="buddy"> <property name="buddy">
<cstring>maxGlobalSearch</cstring> <cstring>max_global_search</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QSpinBox" name="maxGlobalSearch"> <widget class="QSpinBox" name="max_global_search">
<property name="minimum"> <property name="minimum">
<number>50</number> <number>50</number>
</property> </property>
@ -110,6 +110,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="enable_broadcast">
<property name="text">
<string>Show playing song on your page</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@ -19,45 +19,39 @@
#include "core/application.h" #include "core/application.h"
#include "core/logging.h" #include "core/logging.h"
#include "core/player.h"
#include "vkservice.h" #include "vkservice.h"
#include "vkmusiccache.h" #include "vkmusiccache.h"
VkUrlHandler::VkUrlHandler(VkService* service, QObject* parent) VkUrlHandler::VkUrlHandler(VkService* service, QObject* parent)
: UrlHandler(parent), : UrlHandler(parent), service_(service) {}
service_(service) {
}
UrlHandler::LoadResult VkUrlHandler::StartLoading(const QUrl& url) { UrlHandler::LoadResult VkUrlHandler::StartLoading(const QUrl& url) {
QStringList args = url.path().split("/"); QStringList args = url.path().split("/");
LoadResult result; LoadResult result;
if (args.size() < 2) { if (args.size() < 2) {
qLog(Error) << "Invalid Vk.com URL: " << url qLog(Error)
<< "Invalid Vk.com URL: " << url
<< "Url format should be vk://<source>/<id>." << "Url format should be vk://<source>/<id>."
<< "For example vk://song/61145020_166946521/Daughtry/Gone Too Soon"; << "For example vk://song/61145020_166946521/Daughtry/Gone Too Soon";
} else { } else {
QString action = url.host(); QString action = url.host();
if (action == "song") { if (action == "song") {
service_->SetCurrentSongFromUrl(url); result = service_->GetSongResult(url);
result = LoadResult(url, LoadResult::TrackAvailable, service_->cache()->Get(url));
} else if (action == "group") { } else if (action == "group") {
result = service_->GetGroupNextSongUrl(url); result = service_->GetGroupNextSongUrl(url);
} else { } else {
qLog(Error) << "Invalid vk.com url action:" << action; qLog(Error) << "Invalid vk.com url action:" << action;
} }
} }
return result; return result;
} }
void VkUrlHandler::TrackSkipped() { void VkUrlHandler::TrackSkipped() { service_->SongSkipped(); }
service_->cache()->BreakCurrentCaching();
}
void VkUrlHandler::ForceAddToCache(const QUrl& url) {
service_->cache()->ForceCache(url);
}
UrlHandler::LoadResult VkUrlHandler::LoadNext(const QUrl& url) { UrlHandler::LoadResult VkUrlHandler::LoadNext(const QUrl& url) {
if (url.host() == "group") { if (url.host() == "group") {

View File

@ -34,7 +34,6 @@ public:
QIcon icon() const { return QIcon(":providers/vk.png"); } QIcon icon() const { return QIcon(":providers/vk.png"); }
LoadResult StartLoading(const QUrl& url); LoadResult StartLoading(const QUrl& url);
void TrackSkipped(); void TrackSkipped();
void ForceAddToCache(const QUrl& url);
LoadResult LoadNext(const QUrl& url); LoadResult LoadNext(const QUrl& url);
private: private: