#include "patchdownloader.h" #include "models/filesystem.h" #include #include #include #include #include #include PatchDownloader::PatchDownloader(QObject *parent) : QObject(parent) { patch_download_dir = QDir(QApplication::applicationDirPath() + "/data"); connect(&update_check_timer, &QTimer::timeout, this, &PatchDownloader::checkForUpdates); update_check_timer.setInterval(1000 * 60 * 5); // 5 minutes update_check_timer.start(); } PatchDownloader::~PatchDownloader() { clearDownloadQueue(); } void PatchDownloader::checkForUpdates() { if (!download_queue.empty()) { qDebug() << "PatchDownloader: downloads are not ready yet, passing checkForUpdates"; return; } if (!updatePatchList()) return; removeOldPatchesFromDirecrory(); addMissingPatchesToDownloadList(); } void PatchDownloader::onDownloaderCompleted(Downloader *downloader_ptr) { downloader_ptr->targetFile->close(); downloader_ptr->targetFile->deleteLater(); } void PatchDownloader::onDownloaderProgressChanged(Downloader*) { quint64 totalSize = 0; quint64 downloadedSize = 0; quint64 summary_speed = 0; quint64 time_elapsed = 0; foreach (Downloader* downloader, download_queue) { totalSize += downloader->getBytesTotal(); downloadedSize += downloader->getBytesDownloaded(); if (downloader->getBytesTotal() != downloader->getBytesDownloaded()) { summary_speed += downloader->getSpeed(); } } time_elapsed = (totalSize - downloadedSize) / qMax(quint64(1), summary_speed); if (totalSize == downloadedSize) emit downloadCompleted(); else emit progressChanged(downloadedSize, totalSize, Downloader::getSpeedFormatted(summary_speed), Downloader::getElapsedTimeFormatted(time_elapsed)); } int PatchDownloader::versionFromPatchFilename(QString filename) { int version = 0; for (int i = filename.indexOf("_v") + 2; i < filename.indexOf("_v") + 7; i += 2) { version = version * 10 + (filename.at(i).toLatin1() - '0'); } return version; } bool PatchDownloader::updatePatchList() { QUrlQuery query; // query for building GET-request aka patch-version QStringList names; names << "sound" << "text" << "image" << "loadscreen" << "texture" << "font"; foreach(QString patch_name, names) { query.addQueryItem(patch_name, "100"); } QUrl target_url; target_url.setUrl("http://translate.lotros.ru/groupware/check_updates"); target_url.setQuery(query); QByteArray target_array; Downloader downloader; downloader.setUrl(target_url); downloader.targetBytearray = &target_array; downloader.start(); downloader.waitForDownloaded(); if (target_array.isEmpty()) { qDebug() << __FUNCTION__ << "Cannot download, target_array is empty!"; emit getPatchListError(); return false; } QStringList entry_list = QString(target_array).split('|'); if (entry_list.size() != names.size()) { qDebug() << __FUNCTION__ << "Entry list size is not equal to patch names size!" << QString(target_array); emit getPatchListError(); return false; } patches.clear(); for (int i = 0; i < entry_list.size(); ++i) { QStringList patch_data = entry_list[i].split(":::"); if (patch_data.size() != 3) { qDebug() << __FUNCTION__ << "Incorrect patch entry size! Entry: " << entry_list[i]; emit getPatchListError(); return false; } patches.append({patch_data[0], patch_data[1], patch_data[2]}); } return true; } bool PatchDownloader::removeOldPatchesFromDirecrory() { QStringList actual_hash_list; foreach (Patch patch, patches) { actual_hash_list.append(patch.md5_hash); } qDebug() << "Removing old patches. Current hash list " << actual_hash_list; QStringList paths = patch_download_dir.entryList(QDir::Files); foreach (QString filename, paths) { QString hash = FileSystem::fileHash(patch_download_dir.absolutePath() + "/" + filename, QCryptographicHash::Md5); if (!actual_hash_list.contains(hash)) { qDebug() << "File " << filename << " with hash " << hash << "seems outdated, deleting!"; if (!QFile::remove(patch_download_dir.absolutePath() + "/" +filename)) { qDebug() << __FUNCTION__ << "Unable to remove file " << filename; emit removeFileError(patch_download_dir.absolutePath() + "/" + filename); } } } return true; } bool PatchDownloader::addMissingPatchesToDownloadList() { QStringList file_hashes; QStringList paths = patch_download_dir.entryList(QDir::Files); QDir dir(patch_download_dir); if (!dir.exists()) QDir().mkdir(patch_download_dir.absolutePath()); foreach (QString filename, paths) { file_hashes << FileSystem::fileHash(patch_download_dir.absolutePath() + "/" + filename, QCryptographicHash::Md5); } bool download_started = false; foreach (Patch patch, patches) { if (file_hashes.contains(patch.md5_hash)) continue; QString patch_filepath = patch_download_dir.absolutePath() + "/" + patch.url.fileName(); if (FileSystem::fileExists(patch_filepath) && !QFile::remove(patch_filepath)) { qDebug() << __FUNCTION__ << "Unable to remove file " << patch_filepath; emit removeFileError(patch_filepath); continue; } if (!download_started) { download_started = true; emit downloadStarted(); } qDebug() << "Starting download of file " << patch_filepath << " from url " << patch.url; Downloader* downloader = new Downloader(); connect(downloader, &Downloader::progressChanged, this, &PatchDownloader::onDownloaderProgressChanged); connect(downloader, &Downloader::downloadFinished, this, &PatchDownloader::onDownloaderCompleted); downloader->setUrl(patch.url); downloader->targetFile = new QFile(patch_filepath, downloader); downloader->targetFile->open(QIODevice::ReadWrite); downloader->start(); download_queue.append(downloader); } return true; } void PatchDownloader::clearDownloadQueue() { for (int i = 0; i < download_queue.size(); ++i) { Downloader* downloader = download_queue[i]; if (download_queue.indexOf(downloader) != i) continue; // this downloader was already stopped and deleted in previous iterations. // this shouldn't normally happen because it means that file was being downloaded many times downloader->stop(); downloader->targetFile->close(); downloader->targetFile->deleteLater(); downloader->deleteLater(); } download_queue.clear(); }