mirror of https://github.com/stenzek/duckstation
Qt: Add cover downloader
parent
21b7261dc9
commit
389143db64
@ -0,0 +1,116 @@
|
||||
#include "coverdownloaddialog.h"
|
||||
#include "common/assert.h"
|
||||
#include "frontend-common/game_list.h"
|
||||
|
||||
CoverDownloadDialog::CoverDownloadDialog(QWidget* parent /*= nullptr*/) : QDialog(parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
updateEnabled();
|
||||
|
||||
connect(m_ui.start, &QPushButton::clicked, this, &CoverDownloadDialog::onStartClicked);
|
||||
connect(m_ui.close, &QPushButton::clicked, this, &CoverDownloadDialog::onCloseClicked);
|
||||
connect(m_ui.urls, &QTextEdit::textChanged, this, &CoverDownloadDialog::updateEnabled);
|
||||
}
|
||||
|
||||
CoverDownloadDialog::~CoverDownloadDialog()
|
||||
{
|
||||
Assert(!m_thread);
|
||||
}
|
||||
|
||||
void CoverDownloadDialog::closeEvent(QCloseEvent* ev)
|
||||
{
|
||||
cancelThread();
|
||||
}
|
||||
|
||||
void CoverDownloadDialog::onDownloadStatus(const QString& text)
|
||||
{
|
||||
m_ui.status->setText(text);
|
||||
}
|
||||
|
||||
void CoverDownloadDialog::onDownloadProgress(int value, int range)
|
||||
{
|
||||
// limit to once every five seconds
|
||||
if (m_last_refresh_time.GetTimeSeconds() >= 5.0f)
|
||||
{
|
||||
emit coverRefreshRequested();
|
||||
m_last_refresh_time.Reset();
|
||||
}
|
||||
|
||||
if (range != m_ui.progress->maximum())
|
||||
m_ui.progress->setMaximum(range);
|
||||
m_ui.progress->setValue(value);
|
||||
}
|
||||
|
||||
void CoverDownloadDialog::onDownloadComplete()
|
||||
{
|
||||
emit coverRefreshRequested();
|
||||
|
||||
if (m_thread)
|
||||
{
|
||||
m_thread->join();
|
||||
m_thread.reset();
|
||||
}
|
||||
|
||||
updateEnabled();
|
||||
|
||||
m_ui.status->setText(tr("Download complete."));
|
||||
}
|
||||
|
||||
void CoverDownloadDialog::onStartClicked()
|
||||
{
|
||||
if (m_thread)
|
||||
cancelThread();
|
||||
else
|
||||
startThread();
|
||||
}
|
||||
|
||||
void CoverDownloadDialog::onCloseClicked()
|
||||
{
|
||||
if (m_thread)
|
||||
cancelThread();
|
||||
|
||||
done(0);
|
||||
}
|
||||
|
||||
void CoverDownloadDialog::updateEnabled()
|
||||
{
|
||||
const bool running = static_cast<bool>(m_thread);
|
||||
m_ui.start->setText(running ? tr("Stop") : tr("Start"));
|
||||
m_ui.start->setEnabled(running || !m_ui.urls->toPlainText().isEmpty());
|
||||
m_ui.close->setEnabled(!running);
|
||||
m_ui.urls->setEnabled(!running);
|
||||
}
|
||||
|
||||
void CoverDownloadDialog::startThread()
|
||||
{
|
||||
m_thread = std::make_unique<CoverDownloadThread>(this, m_ui.urls->toPlainText());
|
||||
connect(m_thread.get(), &CoverDownloadThread::statusUpdated, this, &CoverDownloadDialog::onDownloadStatus);
|
||||
connect(m_thread.get(), &CoverDownloadThread::progressUpdated, this, &CoverDownloadDialog::onDownloadProgress);
|
||||
connect(m_thread.get(), &CoverDownloadThread::threadFinished, this, &CoverDownloadDialog::onDownloadComplete);
|
||||
m_thread->start();
|
||||
updateEnabled();
|
||||
}
|
||||
|
||||
void CoverDownloadDialog::cancelThread()
|
||||
{
|
||||
if (!m_thread)
|
||||
return;
|
||||
|
||||
m_thread->requestInterruption();
|
||||
m_thread->join();
|
||||
m_thread.reset();
|
||||
}
|
||||
|
||||
CoverDownloadDialog::CoverDownloadThread::CoverDownloadThread(QWidget* parent, const QString& urls)
|
||||
: QtAsyncProgressThread(parent)
|
||||
{
|
||||
for (const QString& str : urls.split(QChar('\n')))
|
||||
m_urls.push_back(str.toStdString());
|
||||
}
|
||||
|
||||
CoverDownloadDialog::CoverDownloadThread::~CoverDownloadThread() = default;
|
||||
|
||||
void CoverDownloadDialog::CoverDownloadThread::runAsync()
|
||||
{
|
||||
GameList::DownloadCovers(m_urls, this);
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
#include "common/timer.h"
|
||||
#include "common/types.h"
|
||||
#include "qtprogresscallback.h"
|
||||
#include "ui_coverdownloaddialog.h"
|
||||
#include <QtWidgets/QDialog>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class CoverDownloadDialog final : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CoverDownloadDialog(QWidget* parent = nullptr);
|
||||
~CoverDownloadDialog();
|
||||
|
||||
Q_SIGNALS:
|
||||
void coverRefreshRequested();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent* ev);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onDownloadStatus(const QString& text);
|
||||
void onDownloadProgress(int value, int range);
|
||||
void onDownloadComplete();
|
||||
void onStartClicked();
|
||||
void onCloseClicked();
|
||||
void updateEnabled();
|
||||
|
||||
private:
|
||||
class CoverDownloadThread : public QtAsyncProgressThread
|
||||
{
|
||||
public:
|
||||
CoverDownloadThread(QWidget* parent, const QString& urls);
|
||||
~CoverDownloadThread();
|
||||
|
||||
protected:
|
||||
void runAsync() override;
|
||||
|
||||
private:
|
||||
std::vector<std::string> m_urls;
|
||||
};
|
||||
|
||||
void startThread();
|
||||
void cancelThread();
|
||||
|
||||
Ui::CoverDownloadDialog m_ui;
|
||||
std::unique_ptr<CoverDownloadThread> m_thread;
|
||||
Common::Timer m_last_refresh_time;
|
||||
};
|
@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CoverDownloadDialog</class>
|
||||
<widget class="QDialog" name="CoverDownloadDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>656</width>
|
||||
<height>343</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Download Covers</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>DuckStation can automatically download covers for games which do not currently have a cover set. We do not host any cover images, the user must provide their own source for images.</p><p>In the form below, specify the URLs to download covers from, with one template URL per line. The following variables are available:</p><p><span style=" font-style:italic;">${title}:</span> Title of the game.<br/><span style=" font-style:italic;">${filetitle}:</span> Name component of the game's filename.<br/><span style=" font-style:italic;">${serial}:</span> Serial of the game.</p><p><span style=" font-weight:700;">Example:</span> https://www.example-not-a-real-domain.com/covers/${serial}.jpg</p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="urls"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="status">
|
||||
<property name="text">
|
||||
<string>Waiting to start...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progress"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="start">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="close">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
Loading…
Reference in New Issue