214 lines
6.0 KiB
C++
214 lines
6.0 KiB
C++
|
/* This file is part of Clementine.
|
||
|
Copyright 2013, Vlad Maltsev <shedwardx@gmail.com>
|
||
|
|
||
|
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 <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "vkmusiccache.h"
|
||
|
#include "vkservice.h"
|
||
|
|
||
|
#include <QDir>
|
||
|
|
||
|
#include "core/application.h"
|
||
|
#include "core/logging.h"
|
||
|
#include "core/taskmanager.h"
|
||
|
|
||
|
VkMusicCache::VkMusicCache(Application* app, VkService* service)
|
||
|
: QObject(service),
|
||
|
app_(app),
|
||
|
service_(service),
|
||
|
current_cashing_index(0),
|
||
|
is_downloading(false),
|
||
|
is_aborted(false),
|
||
|
task_id(0),
|
||
|
file_(NULL),
|
||
|
network_manager_(new QNetworkAccessManager),
|
||
|
reply_(NULL) {
|
||
|
}
|
||
|
|
||
|
QUrl VkMusicCache::Get(const QUrl& url) {
|
||
|
QString cached_filename = CachedFilename(url);
|
||
|
QUrl result;
|
||
|
if (InCache(cached_filename)) {
|
||
|
qLog(Info) << "Use cashed file" << 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;
|
||
|
}
|
||
|
|
||
|
void VkMusicCache::ForceCache(const QUrl& url) {
|
||
|
AddToQueue(CachedFilename(url), service_->GetSongPlayUrl(url));
|
||
|
}
|
||
|
|
||
|
void VkMusicCache::BreakCurrentCaching() {
|
||
|
if (current_cashing_index > 0) {
|
||
|
// Current song in queue
|
||
|
queue_.removeAt(current_cashing_index - 1);
|
||
|
} else if (current_cashing_index == 0) {
|
||
|
// Current song is downloading
|
||
|
if (reply_) {
|
||
|
reply_->abort();
|
||
|
is_aborted = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/***
|
||
|
* Queue operations
|
||
|
*/
|
||
|
|
||
|
void VkMusicCache::AddToQueue(const QString& filename, const QUrl& download_url) {
|
||
|
DownloadItem item;
|
||
|
item.filename = filename;
|
||
|
item.url = download_url;
|
||
|
queue_.push_back(item);
|
||
|
DownloadNext();
|
||
|
}
|
||
|
|
||
|
/***
|
||
|
* Downloading
|
||
|
*/
|
||
|
|
||
|
void VkMusicCache::DownloadNext() {
|
||
|
if (is_downloading || queue_.isEmpty()) {
|
||
|
return;
|
||
|
} else {
|
||
|
current_download = queue_.first();
|
||
|
queue_.pop_front();
|
||
|
current_cashing_index--;
|
||
|
|
||
|
// Check file path and file existance first
|
||
|
if (QFile::exists(current_download.filename)) {
|
||
|
qLog(Warning) << "Tried to overwrite already cached file" << current_download.filename;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Create temporarry file we download to.
|
||
|
if (file_) {
|
||
|
qLog(Warning) << "QFile" << file_->fileName() << "is not null";
|
||
|
delete file_;
|
||
|
file_ = NULL;
|
||
|
}
|
||
|
|
||
|
file_ = new QTemporaryFile;
|
||
|
if (!file_->open(QFile::WriteOnly)) {
|
||
|
qLog(Warning) << "Can not create temporary file" << file_->fileName()
|
||
|
<< "Download right away to" << current_download.filename;
|
||
|
}
|
||
|
|
||
|
// Start downloading
|
||
|
is_aborted = false;
|
||
|
is_downloading = true;
|
||
|
task_id = app_->task_manager()->
|
||
|
StartTask(tr("Caching %1")
|
||
|
.arg(QFileInfo(current_download.filename).baseName()));
|
||
|
reply_ = network_manager_->get(QNetworkRequest(current_download.url));
|
||
|
connect(reply_, SIGNAL(finished()), SLOT(Downloaded()));
|
||
|
connect(reply_, SIGNAL(readyRead()), SLOT(DownloadReadyToRead()));
|
||
|
connect(reply_, SIGNAL(downloadProgress(qint64, qint64)), SLOT(DownloadProgress(qint64, qint64)));
|
||
|
qLog(Info)<< "Start cashing" << current_download.filename << "from" << current_download.url;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void VkMusicCache::DownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
||
|
if (bytesTotal) {
|
||
|
int progress = round(100 * bytesReceived / bytesTotal);
|
||
|
app_->task_manager()->SetTaskProgress(task_id, progress, 100);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void VkMusicCache::DownloadReadyToRead() {
|
||
|
if (file_) {
|
||
|
file_->write(reply_->readAll());
|
||
|
} else {
|
||
|
qLog(Warning) << "Tried to write recived song to not created file";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void VkMusicCache::Downloaded() {
|
||
|
app_->task_manager()->SetTaskFinished(task_id);
|
||
|
if (is_aborted || reply_->error()) {
|
||
|
if (reply_->error()) {
|
||
|
qLog(Info) << "Downloading failed" << reply_->errorString();
|
||
|
}
|
||
|
} else {
|
||
|
DownloadReadyToRead(); // Save all recent recived data.
|
||
|
|
||
|
QString path = service_->cacheDir();
|
||
|
|
||
|
if (file_->size() > 0) {
|
||
|
QDir(path).mkpath(QFileInfo(current_download.filename).path());
|
||
|
if (file_->copy(current_download.filename)) {
|
||
|
qLog(Info) << "Cached" << current_download.filename;
|
||
|
} else {
|
||
|
qLog(Error) << "Unable to save" << current_download.filename
|
||
|
<< ":" << file_->errorString();
|
||
|
}
|
||
|
} else {
|
||
|
qLog(Error) << "File" << current_download.filename << "is empty";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delete file_;
|
||
|
file_ = NULL;
|
||
|
|
||
|
reply_->deleteLater();
|
||
|
reply_ = NULL;
|
||
|
|
||
|
is_downloading = false;
|
||
|
DownloadNext();
|
||
|
}
|
||
|
|
||
|
/***
|
||
|
* Utils
|
||
|
*/
|
||
|
|
||
|
bool VkMusicCache::InCache(const QString& filename) {
|
||
|
return QFile::exists(filename);
|
||
|
}
|
||
|
|
||
|
bool VkMusicCache::InCache(const QUrl& url) {
|
||
|
return InCache(CachedFilename(url));
|
||
|
}
|
||
|
|
||
|
QString VkMusicCache::CachedFilename(const QUrl& url) {
|
||
|
QStringList args = url.path().split('/');
|
||
|
|
||
|
QString cache_filename;
|
||
|
if (args.size() == 4) {
|
||
|
cache_filename = service_->cacheFilename();
|
||
|
cache_filename.replace("%artist", args[2]);
|
||
|
cache_filename.replace("%title", args[3]);
|
||
|
} else {
|
||
|
qLog(Warning) << "Song url with args" << args << "does not contain artist and title"
|
||
|
<< "use id as file name for cache.";
|
||
|
cache_filename = args[1];
|
||
|
}
|
||
|
|
||
|
QString cache_dir = service_->cacheDir();
|
||
|
if (cache_dir.isEmpty()) {
|
||
|
qLog(Warning) << "Cache dir not defined";
|
||
|
return "";
|
||
|
}
|
||
|
// TODO(Vk): Maybe use extenstion from link? Seems it's always mp3.
|
||
|
return cache_dir+QDir::separator()+cache_filename+".mp3";
|
||
|
}
|