Added ability to fetch song cover from external resource.

This commit is contained in:
23rd 2021-01-11 16:58:25 +03:00 committed by John Preston
parent 3fadf2ee54
commit ad761011d6
4 changed files with 177 additions and 0 deletions

View File

@ -961,6 +961,8 @@ PRIVATE
storage/storage_account.h
storage/storage_cloud_blob.cpp
storage/storage_cloud_blob.h
storage/storage_cloud_song_cover.cpp
storage/storage_cloud_song_cover.h
storage/storage_domain.cpp
storage/storage_domain.h
storage/storage_facade.cpp

View File

@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/media/history_view_gif.h"
#include "window/window_session_controller.h"
#include "storage/cache/storage_cache_database.h"
#include "storage/storage_cloud_song_cover.h"
#include "boxes/confirm_box.h"
#include "ui/image/image.h"
#include "ui/text/text_utilities.h"
@ -540,6 +541,13 @@ void DocumentData::setattributes(
songData->duration = data.vduration().v;
songData->title = qs(data.vtitle().value_or_empty());
songData->performer = qs(data.vperformer().value_or_empty());
if (!hasThumbnail()
&& !songData->title.isEmpty()
&& !songData->performer.isEmpty()) {
Storage::CloudSongCover::LoadThumbnailFromExternal(this);
}
}
}, [&](const MTPDdocumentAttributeFilename &data) {
_filename = qs(data.vfile_name());

View File

@ -0,0 +1,151 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "storage/storage_cloud_song_cover.h"
#include "data/data_cloud_file.h"
#include "data/data_document.h"
#include "data/data_file_origin.h"
#include "data/data_session.h"
#include "main/main_session.h"
#include "storage/file_download.h"
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonValue>
namespace Storage::CloudSongCover {
namespace {
constexpr auto kMaxResponseSize = 1024 * 1024;
constexpr auto kDefaultCoverSize = 100;
struct Responce {
const QString artworkUrl;
const int size;
};
auto Location(const QString &url) {
return DownloadLocation{ PlainUrlLocation{ url } };
}
auto JsonUrl(not_null<SongData*> song) {
return QString("https://itunes.apple.com/search?term=" \
"%1 %2&entity=song&limit=4").arg(song->performer).arg(song->title);
}
// Dummy JSON responce.
// {
// "resultCount": 2,
// "results": [
// {
// "artworkUrl100": ""
// },
// {
// "artworkUrl100": ""
// }
// ]
// }
std::optional<Responce> ParseResponce(const QByteArray &response) {
if (response.size() >= kMaxResponseSize) {
return std::nullopt;
}
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
const auto document = QJsonDocument::fromJson(response, &error);
const auto log = [](const QString &message) {
DEBUG_LOG(("Parse Artwork JSON Error: %1.").arg(message));
};
if (error.error != QJsonParseError::NoError) {
log(error.errorString());
return std::nullopt;
} else if (!document.isObject()) {
log("not an object received in JSON");
return std::nullopt;
}
const auto results = document.object().value("results");
if (!results.isArray()) {
log("'results' field not found");
return std::nullopt;
}
const auto resultsArray = results.toArray();
if (resultsArray.empty()) {
return std::nullopt;
}
const auto artworkUrl = resultsArray.first().toObject()
.value("artworkUrl100").toString();
if (artworkUrl.isEmpty()) {
log("'artworkUrl100' field is empty");
return std::nullopt;
}
return Responce{ artworkUrl, kDefaultCoverSize };
}
void LoadAndApplyThumbnail(
not_null<DocumentData*> document,
const Responce &responce) {
const auto size = responce.size;
const auto imageWithLocation = ImageWithLocation{
.location = ImageLocation(Location(responce.artworkUrl), size, size)
};
document->updateThumbnails(
QByteArray(),
imageWithLocation,
ImageWithLocation{ .location = ImageLocation() });
document->loadThumbnail(Data::FileOrigin());
}
}
void LoadThumbnailFromExternal(not_null<DocumentData*> document) {
const auto songData = document->song();
if (!songData
|| songData->performer.isEmpty()
|| songData->title.isEmpty()) {
return;
}
const auto &size = kDefaultCoverSize;
const auto jsonLocation = ImageWithLocation{
.location = ImageLocation(Location(JsonUrl(songData)), size, size)
};
const auto jsonCloudFile = std::make_shared<Data::CloudFile>();
Data::UpdateCloudFile(
*jsonCloudFile,
jsonLocation,
document->owner().cache(),
0, // Cache tag.
nullptr,
nullptr);
auto done = [=](const QByteArray &result) {
if (!jsonCloudFile) {
return;
}
if (const auto responce = ParseResponce(result)) {
LoadAndApplyThumbnail(document, *responce);
}
};
Data::LoadCloudFile(
&document->session(),
*jsonCloudFile,
Data::FileOrigin(),
LoadFromCloudOrLocal,
true,
0,
[] { return true; },
std::move(done));
}
} // namespace Storage::CloudSongCover

View File

@ -0,0 +1,16 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class DocumentData;
namespace Storage::CloudSongCover {
void LoadThumbnailFromExternal(not_null<DocumentData*> document);
} // namespace Storage::CloudSongCover