Precache the first and last parts of Google Drive MP3s to reduce the

number of requests when using accurate tagging.
This commit is contained in:
John Maguire 2012-07-30 13:41:29 +02:00
parent fd1d70c644
commit 08286102da
3 changed files with 50 additions and 8 deletions

View File

@ -1,16 +1,16 @@
/* This file is part of Clementine. /* This file is part of Clementine.
Copyright 2012, David Sansome <me@davidsansome.com> Copyright 2012, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
Clementine is distributed in the hope that it will be useful, Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>. along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/ */
@ -25,7 +25,11 @@
#include <taglib/id3v2framefactory.h> #include <taglib/id3v2framefactory.h>
#include <taglib/mpegfile.h> #include <taglib/mpegfile.h>
using TagLib::ByteVector;
namespace {
static const int kTaglibPrefixCacheBytes = 64 * 1024; // Should be enough.
static const int kTaglibSuffixCacheBytes = 2 * 1024;
}
GoogleDriveStream::GoogleDriveStream( GoogleDriveStream::GoogleDriveStream(
const QUrl& url, const QString& filename, const long length, const QUrl& url, const QString& filename, const long length,
@ -37,7 +41,8 @@ GoogleDriveStream::GoogleDriveStream(
auth_(auth), auth_(auth),
cursor_(0), cursor_(0),
network_(network), network_(network),
cache_(length) { cache_(length),
num_requests_(0) {
} }
TagLib::FileName GoogleDriveStream::name() const { TagLib::FileName GoogleDriveStream::name() const {
@ -68,6 +73,25 @@ TagLib::ByteVector GoogleDriveStream::GetCached(int start, int end) {
return ret; return ret;
} }
void GoogleDriveStream::Precache() {
// For reading the tags of an MP3, TagLib tends to request:
// 1. The first 1024 bytes
// 2. Somewhere between the first 2KB and first 60KB
// 3. The last KB or two.
// 4. Somewhere in the first 64KB again
//
// So, if we precache the first 64KB and the last 2KB we should be sorted :-)
// Ideally, we would use bytes=0-655364,-2048 but Google Drive does not seem
// to support multipart byte ranges yet so we have to make do with two
// requests.
seek(0, TagLib::IOStream::Beginning);
readBlock(kTaglibPrefixCacheBytes);
seek(kTaglibSuffixCacheBytes, TagLib::IOStream::End);
readBlock(kTaglibSuffixCacheBytes);
clear();
}
TagLib::ByteVector GoogleDriveStream::readBlock(ulong length) { TagLib::ByteVector GoogleDriveStream::readBlock(ulong length) {
const uint start = cursor_; const uint start = cursor_;
const uint end = qMin(cursor_ + length - 1, length_ - 1); const uint end = qMin(cursor_ + length - 1, length_ - 1);
@ -89,6 +113,7 @@ TagLib::ByteVector GoogleDriveStream::readBlock(ulong length) {
"Range", QString("bytes=%1-%2").arg(start).arg(end).toUtf8()); "Range", QString("bytes=%1-%2").arg(start).arg(end).toUtf8());
QNetworkReply* reply = network_->get(request); QNetworkReply* reply = network_->get(request);
++num_requests_;
QEventLoop loop; QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
@ -103,11 +128,11 @@ TagLib::ByteVector GoogleDriveStream::readBlock(ulong length) {
return bytes; return bytes;
} }
void GoogleDriveStream::writeBlock(const ByteVector&) { void GoogleDriveStream::writeBlock(const TagLib::ByteVector&) {
qLog(Debug) << Q_FUNC_INFO << "not implemented"; qLog(Debug) << Q_FUNC_INFO << "not implemented";
} }
void GoogleDriveStream::insert(const ByteVector&, ulong, ulong) { void GoogleDriveStream::insert(const TagLib::ByteVector&, ulong, ulong) {
qLog(Debug) << Q_FUNC_INFO << "not implemented"; qLog(Debug) << Q_FUNC_INFO << "not implemented";
} }

View File

@ -47,6 +47,17 @@ class GoogleDriveStream : public TagLib::IOStream {
virtual long length(); virtual long length();
virtual void truncate(long); virtual void truncate(long);
google::sparsetable<char>::size_type cached_bytes() const {
return cache_.num_nonempty();
}
int num_requests() const {
return num_requests_;
}
// Use educated guess to request the bytes that TagLib will probably want.
void Precache();
private: private:
bool CheckCache(int start, int end); bool CheckCache(int start, int end);
void FillCache(int start, TagLib::ByteVector data); void FillCache(int start, TagLib::ByteVector data);
@ -63,6 +74,7 @@ class GoogleDriveStream : public TagLib::IOStream {
QNetworkAccessManager* network_; QNetworkAccessManager* network_;
google::sparsetable<char> cache_; google::sparsetable<char> cache_;
int num_requests_;
}; };
#endif // GOOGLEDRIVESTREAM_H #endif // GOOGLEDRIVESTREAM_H

View File

@ -616,10 +616,11 @@ void TagReaderWorker::ReadGoogleDrive(const QUrl& download_url,
GoogleDriveStream* stream = new GoogleDriveStream( GoogleDriveStream* stream = new GoogleDriveStream(
download_url, title, size, access_token, network_); download_url, title, size, access_token, network_);
stream->Precache();
TagLib::MPEG::File tag( TagLib::MPEG::File tag(
stream, // Takes ownership. stream, // Takes ownership.
TagLib::ID3v2::FrameFactory::instance(), TagLib::ID3v2::FrameFactory::instance(),
TagLib::AudioProperties::Fast); TagLib::AudioProperties::Accurate);
if (tag.tag()) { if (tag.tag()) {
song->set_title(tag.tag()->title().toCString(true)); song->set_title(tag.tag()->title().toCString(true));
song->set_artist(tag.tag()->artist().toCString(true)); song->set_artist(tag.tag()->artist().toCString(true));
@ -631,6 +632,10 @@ void TagReaderWorker::ReadGoogleDrive(const QUrl& download_url,
if (tag.audioProperties()) { if (tag.audioProperties()) {
song->set_length_nanosec(tag.audioProperties()->length() * kNsecPerSec); song->set_length_nanosec(tag.audioProperties()->length() * kNsecPerSec);
} }
qLog(Debug) << "Google Drive Tagging Stats for:"
<< song->title().c_str();
qLog(Debug) << "Downloaded bytes:" << stream->cached_bytes()
<< "Number of requests:" << stream->num_requests();
} }
} }
#endif // HAVE_GOOGLE_DRIVE #endif // HAVE_GOOGLE_DRIVE