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