/* 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 #include #include #include 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 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 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 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 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::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