changeset 230:2f5a9247e501

torrents: implement download button erg
author Paper <paper@paper.us.eu.org>
date Sat, 13 Jan 2024 09:42:02 -0500
parents adc20fa321c1
children 69f4768a820c
files dep/animia/src/fd/xnu.cc include/core/filesystem.h include/core/http.h include/gui/pages/torrents.h src/core/filesystem.cc src/core/http.cc src/core/strings.cc src/gui/pages/anime_list.cc src/gui/pages/torrents.cc src/gui/theme.cc src/gui/widgets/poster.cc src/track/media.cc
diffstat 12 files changed, 183 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/dep/animia/src/fd/xnu.cc	Wed Jan 10 21:23:57 2024 -0500
+++ b/dep/animia/src/fd/xnu.cc	Sat Jan 13 09:42:02 2024 -0500
@@ -17,12 +17,12 @@
 namespace animia::internal::xnu {
 
 bool EnumerateOpenProcesses(process_proc_t process_proc) {
-	size_t pids_size = 512;
+	size_t pids_size = 256;
 	std::unique_ptr<pid_t[]> pids;
 
 	int returned_size = 0;
 	do {
-		pids.reset(new pid_t[pids_size]);
+		pids.reset(new pid_t[pids_size *= 2]);
 		returned_size = proc_listpids(PROC_ALL_PIDS, 0, pids.get(), pids_size * sizeof(pid_t));
 		if (returned_size == -1)
 			return false;
--- a/include/core/filesystem.h	Wed Jan 10 21:23:57 2024 -0500
+++ b/include/core/filesystem.h	Sat Jan 13 09:42:02 2024 -0500
@@ -6,11 +6,10 @@
 namespace Filesystem {
 
 void CreateDirectories(const std::filesystem::path& path);
-std::filesystem::path GetDotPath();        // %APPDATA%/minori, ~/Library/Application Support/minori, ~/.config/minori...
+std::filesystem::path GetDotPath();        // %APPDATA%/minori/, ~/Library/Application Support/minori/, ~/.config/minori/...
 std::filesystem::path GetConfigPath();     // (dotpath)/config.json
 std::filesystem::path GetAnimeDBPath();    // (dotpath)/anime/db.json
-std::filesystem::path GetPlayersPath();    // (dotpath)/player.json
-std::filesystem::path GetExtensionsPath(); // (dotpath)/extensions.json
+std::filesystem::path GetTorrentsPath();   // (dotpath)/torrents/...
 
 } // namespace Filesystem
 
--- a/include/core/http.h	Wed Jan 10 21:23:57 2024 -0500
+++ b/include/core/http.h	Sat Jan 13 09:42:02 2024 -0500
@@ -2,6 +2,7 @@
 #define __core__http_h
 
 #include <QByteArray>
+#include <QThread>
 #include <string>
 #include <vector>
 
@@ -10,6 +11,50 @@
 QByteArray Get(const std::string& url, const std::vector<std::string>& headers = {});
 QByteArray Post(const std::string& url, const std::string& data, const std::vector<std::string>& headers = {});
 
+class GetThread : public QThread {
+	Q_OBJECT
+
+public:
+	GetThread(const std::string& u, const std::vector<std::string>& h = {}) {
+		url = u;
+		headers = h;
+	}
+
+signals:
+	void ReceivedData(const QByteArray& ba);
+
+protected:
+	void run() override {
+		emit ReceivedData(Get(url, headers));
+	}
+
+	std::string url;
+	std::vector<std::string> headers;
+};
+
+class PostThread : public QThread {
+	Q_OBJECT
+
+public:
+	PostThread(const std::string& u, const std::string& d, const std::vector<std::string>& h = {}) {
+		url = u;
+		data = d;
+		headers = h;
+	}
+
+signals:
+	void ReceivedData(const QByteArray& ba);
+
+protected:
+	void run() override {
+		emit ReceivedData(Post(url, data, headers));
+	}
+
+	std::string url;
+	std::string data;
+	std::vector<std::string> headers;
+};
+
 } // namespace HTTP
 
 #endif // __core__http_h
--- a/include/gui/pages/torrents.h	Wed Jan 10 21:23:57 2024 -0500
+++ b/include/gui/pages/torrents.h	Sat Jan 13 09:42:02 2024 -0500
@@ -5,6 +5,9 @@
 #include <QFrame>
 #include <QAbstractListModel>
 #include <QSortFilterProxyModel>
+#include <QItemSelection>
+
+class QTreeView;
 
 class TorrentsPageListSortFilter final : public QSortFilterProxyModel {
 		Q_OBJECT
@@ -46,6 +49,7 @@
 		Qt::ItemFlags flags(const QModelIndex& index) const override;
 
 		QByteArray DownloadTorrentList();
+		void DownloadTorrents(QItemSelection selection);
 		void ParseFeedDescription(const std::string& description, Torrent& torrent);
 		void ParseTorrentList(const QByteArray& ba);
 		void RefreshTorrentList();
@@ -68,11 +72,13 @@
 
 	public:
 		TorrentsPage(QWidget* parent = nullptr);
+		void DownloadSelection();
 		void Refresh();
 
 	private:
 		TorrentsPageListModel* model = nullptr;
 		TorrentsPageListSortFilter* sort_model = nullptr;
+		QTreeView* treeview = nullptr;
 };
 
 #endif // __gui__pages__torrents_h
--- a/src/core/filesystem.cc	Wed Jan 10 21:23:57 2024 -0500
+++ b/src/core/filesystem.cc	Sat Jan 13 09:42:02 2024 -0500
@@ -72,16 +72,12 @@
 	return GetDotPath() / CONFIG_NAME;
 }
 
-std::filesystem::path GetPlayersPath() {
-	return GetDotPath() / "recognition" / "players.json";
-}
-
-std::filesystem::path GetExtensionsPath() {
-	return GetDotPath() / "recognition" / "extensions.json";
-}
-
 std::filesystem::path GetAnimeDBPath() {
 	return GetDotPath() / "anime" / "db.json";
 }
 
+std::filesystem::path GetTorrentsPath() {
+	return GetDotPath() / "torrents";
+}
+
 } // namespace Filesystem
--- a/src/core/http.cc	Wed Jan 10 21:23:57 2024 -0500
+++ b/src/core/http.cc	Sat Jan 13 09:42:02 2024 -0500
@@ -65,3 +65,5 @@
 }
 
 } // namespace HTTP
+
+#include "core/moc_http.cpp"
--- a/src/core/strings.cc	Wed Jan 10 21:23:57 2024 -0500
+++ b/src/core/strings.cc	Sat Jan 13 09:42:02 2024 -0500
@@ -16,6 +16,7 @@
 #include <string>
 #include <vector>
 #include <unordered_map>
+#include <iostream>
 
 namespace Strings {
 
@@ -132,8 +133,15 @@
 }
 
 std::wstring ToWstring(const std::string& string) {
-	static std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
-	return converter.from_bytes(string);
+	static std::wstring_convert<std::codecvt_utf8<wchar_t>> converter("", L"");
+
+	std::wstring wstr;
+	try {
+		wstr = converter.from_bytes(string);
+	} catch (std::range_error const& ex) {
+		std::cerr << "Failed to convert UTF-8 to wide string!" << std::endl;
+	}
+	return wstr;
 }
 
 std::wstring ToWstring(const QString& string) {
@@ -143,7 +151,7 @@
 }
 
 std::string ToUtf8String(const std::wstring& wstring) {
-	static std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
+	static std::wstring_convert<std::codecvt_utf8<wchar_t>> converter("", L"");
 	return converter.to_bytes(wstring);
 }
 
--- a/src/gui/pages/anime_list.cc	Wed Jan 10 21:23:57 2024 -0500
+++ b/src/gui/pages/anime_list.cc	Sat Jan 13 09:42:02 2024 -0500
@@ -28,7 +28,7 @@
 #include <QTreeView>
 #include <QStylePainter>
 #include <QStyledItemDelegate>
-#include <QThreadPool>
+#include <QThread>
 
 #include <set>
 
@@ -218,10 +218,14 @@
 }
 
 void AnimeListPage::UpdateAnime(int id) {
-	QThreadPool::globalInstance()->start([this, id] {
+	QThread* thread = QThread::create([id] {
 		Services::UpdateAnimeEntry(id);
-		Refresh();
 	});
+
+	connect(thread, &QThread::finished, this, &AnimeListPage::Refresh);
+	connect(thread, &QThread::finished, this, &QThread::deleteLater);
+
+	thread->start();
 }
 
 void AnimeListPage::RemoveAnime(int id) {
--- a/src/gui/pages/torrents.cc	Wed Jan 10 21:23:57 2024 -0500
+++ b/src/gui/pages/torrents.cc	Sat Jan 13 09:42:02 2024 -0500
@@ -2,6 +2,7 @@
 #include "core/strings.h"
 #include "core/http.h"
 #include "core/session.h"
+#include "core/filesystem.h"
 #include "gui/widgets/text.h"
 #include "track/media.h"
 
@@ -11,11 +12,12 @@
 #include <QMainWindow>
 #include <QByteArray>
 #include <QDataStream>
-#include <QThreadPool>
+#include <QThread>
 #include <QDebug>
 
 #include <iostream>
 #include <sstream>
+#include <fstream>
 #include <algorithm>
 
 #include "pugixml.hpp"
@@ -50,6 +52,28 @@
 TorrentsPageListModel::TorrentsPageListModel(QObject* parent) : QAbstractListModel(parent) {
 }
 
+void TorrentsPageListModel::DownloadTorrents(QItemSelection selection) {
+	const auto indexes = selection.indexes();
+
+	for (const auto& index : indexes) {
+		/* a torrent file IS literally text... */
+		const std::string link = list.at(index.row()).GetLink();
+		const std::string filename = list.at(index.row()).GetFilename() + ".torrent";
+
+		const std::filesystem::path torrents_dir = Filesystem::GetTorrentsPath();
+		std::filesystem::create_directories(torrents_dir);
+
+		std::ofstream file(torrents_dir / filename, std::ofstream::out | std::ofstream::trunc);
+		if (!file)
+			return; // wat
+
+		const QByteArray data = HTTP::Get(link);
+		file.write(data.data(), data.size());
+
+		file.close();
+	}
+}
+
 QByteArray TorrentsPageListModel::DownloadTorrentList() {
 	return HTTP::Get(session.config.torrents.feed_link);
 }
@@ -231,14 +255,15 @@
 				case TL_DESCRIPTION: return Strings::ToQString(item.GetDescription());
 				case TL_FILENAME: return Strings::ToQString(item.GetFilename());
 				case TL_RELEASEDATE: return item.GetDate();
-				default: return "";
+				default: return {};
 			}
 			break;
 		case Qt::UserRole:
 			switch (index.column()) {
 				case TL_EPISODE: return Strings::ToInt(item.GetEpisode(), -1);
 				/* We have to use this to work around some stupid
-				   "conversion ambiguous" error on Linux */
+				 * "conversion ambiguous" error on Linux
+				*/
 				case TL_SIZE: return QVariant::fromValue(item.GetSize());
 				default: return data(index, Qt::DisplayRole);
 			}
@@ -300,17 +325,17 @@
 		{
 			/* this needs to be stored somewhere to replicate Taiga's
 			   "timer" feature */
-			toolbar->addAction(QIcon(":/icons/16x16/arrow-circle-315.png"), tr("&Check new torrents"), [this]{
-				QThreadPool::globalInstance()->start([this] {
-					Refresh();
-				});
+			toolbar->addAction(QIcon(":/icons/16x16/arrow-circle-315.png"), tr("&Check new torrents"), [this] {
+				Refresh();
 			});
 		}
 
 		toolbar->addSeparator();
 
 		{
-			toolbar->addAction(QIcon(":/icons/16x16/navigation-270-button.png"), tr("Download &marked torrents"));
+			toolbar->addAction(QIcon(":/icons/16x16/navigation-270-button.png"), tr("Download &marked torrents"), [this] {
+				DownloadSelection();
+			});
 		}
 
 		{
@@ -335,7 +360,7 @@
 	}
 
 	{
-		QTreeView* treeview = new QTreeView(this);
+		treeview = new QTreeView(this);
 		treeview->setUniformRowHeights(true);
 		treeview->setAllColumnsShowFocus(false);
 		treeview->setAlternatingRowColors(true);
@@ -359,10 +384,44 @@
 	}
 }
 
+void TorrentsPage::DownloadSelection() {
+	/* we only want one of these at a time, because if we don't
+	 * we have the possibility of going into Multithreading Hell
+	*/
+	static QThread* thread = nullptr;
+
+	if (!model || thread)
+		return;
+
+	const QItemSelection selection = sort_model->mapSelectionToSource(treeview->selectionModel()->selection());
+
+	thread = QThread::create([this, selection] {
+		model->DownloadTorrents(selection);
+	});
+
+	connect(thread, &QThread::finished, thread, &QThread::deleteLater);
+	connect(thread, &QThread::finished, this, [&] { thread = nullptr; });
+
+	thread->start();
+}
+
 void TorrentsPage::Refresh() {
 	if (!model)
 		return;
-	model->RefreshTorrentList();
+
+	HTTP::GetThread* thread = new HTTP::GetThread(session.config.torrents.feed_link);
+
+	connect(thread, &HTTP::GetThread::ReceivedData, this, [&](const QByteArray& ba) {
+		/* This is to make sure we aren't in a different thread
+		 * messing around with GUI stuff
+		*/
+		treeview->setUpdatesEnabled(false);
+		model->ParseTorrentList(ba);
+		treeview->setUpdatesEnabled(true);
+	});
+	connect(thread, &QThread::finished, thread, &QThread::deleteLater);
+
+	thread->start();
 }
 
 #include "gui/pages/moc_torrents.cpp"
--- a/src/gui/theme.cc	Wed Jan 10 21:23:57 2024 -0500
+++ b/src/gui/theme.cc	Sat Jan 13 09:42:02 2024 -0500
@@ -16,9 +16,6 @@
  *   2. Some widgets, i.e. QTabWidget, QTabBar, etc., just completely IGNORE the QPalette setting
  *      on different platforms and the only way to fix it is by using Fusion
  *   3. Windows dark mode support in Qt 6.5 (with Fusion) is completely unavoidable
- * I think what I might end up doing is forcing the Fusion style on any platforms that isn't
- * Windows or Mac. I'm not really fond of doing that, but it's the best way to achieve a "good"
- * visual style without a substaintial amount of fucking around and subsequent finding out.
 */
 
 namespace Theme {
@@ -88,10 +85,10 @@
 void Theme::SetStyleSheet(Themes theme) {
 	switch (theme) {
 		case Themes::DARK: {
-			QColor darkGray(53, 53, 53);
-			QColor gray(128, 128, 128);
-			QColor black(25, 25, 25);
-			QColor blue(42, 130, 218);
+			const QColor darkGray(53, 53, 53);
+			const QColor gray(128, 128, 128);
+			const QColor black(25, 25, 25);
+			const QColor blue(42, 130, 218);
 
 			QPalette pal(QApplication::style()->standardPalette());
 			pal.setColor(QPalette::Window, darkGray);
@@ -115,22 +112,20 @@
 			qApp->setPalette(pal);
 
 #ifdef WIN32
-			/* This is a dark style sheet that makes things look
-			 * marginally better on Windows.
-			 *
-			 * I'm very close to just giving up and using Fusion
-			 * everywhere.
-			*/
-			QFile f(":dark.qss");
-			if (!f.exists())
-				break; // how?
-			f.open(QFile::ReadOnly | QFile::Text);
-			QTextStream ts(&f);
-			qApp->setStyleSheet(ts.readAll());
+			qApp->setStyleSheet([]{
+				QFile f(":/dark.qss");
+				if (!f.exists())
+					return QStringLiteral("");
+				f.open(QFile::ReadOnly | QFile::Text);
+				QTextStream ts(&f);
+				return ts.readAll();
+			}());
 #endif
 			break;
 		}
 		default:
+			/* this sucks, it relies on the standard palette which
+			 * may or may not be a dark style itself. */
 			QPalette pal(QApplication::style()->standardPalette());
 #ifdef WIN32 /* fuck you Qt 6 */
 			pal.setColor(QPalette::Window, QColor(0xF0, 0xF0, 0xF0));
--- a/src/gui/widgets/poster.cc	Wed Jan 10 21:23:57 2024 -0500
+++ b/src/gui/widgets/poster.cc	Sat Jan 13 09:42:02 2024 -0500
@@ -12,7 +12,7 @@
 #include <QLabel>
 #include <QMessageBox>
 #include <QPixmap>
-#include <QThreadPool>
+#include <QThread>
 #include <QUrl>
 #include <curl/curl.h>
 
@@ -35,10 +35,19 @@
 }
 
 void Poster::SetAnime(const Anime::Anime& anime) {
-	QThreadPool::globalInstance()->start([this, anime] {
-		QByteArray ba = HTTP::Get(anime.GetPosterUrl(), {});
-		ImageDownloadFinished(ba);
-	});
+	{
+		QByteArray ba;
+
+		QThread* thread = QThread::create([&] {
+			ba = HTTP::Get(anime.GetPosterUrl(), {});
+		});
+
+		connect(thread, &QThread::finished, this, [&] {
+			ImageDownloadFinished(ba);
+		});
+
+		thread->start();
+	}
 
 	label->disconnect();
 	connect(label, &ClickableLabel::clicked, this,
--- a/src/track/media.cc	Wed Jan 10 21:23:57 2024 -0500
+++ b/src/track/media.cc	Sat Jan 13 09:42:02 2024 -0500
@@ -11,6 +11,8 @@
 #include <vector>
 #include <filesystem>
 
+#include <iostream>
+
 #include "animia.h"
 
 namespace Track {
@@ -44,9 +46,13 @@
 			for (const auto& info : media.information) {
 				switch (info.type) {
 					case animia::MediaInfoType::File:
+						vec.push_back(std::filesystem::path(info.value).filename().u8string());
+						success |= true;
+						break;
 					case animia::MediaInfoType::Title:
-						vec.push_back(std::filesystem::path(info.value).filename().string());
+						vec.push_back(info.value);
 						success |= true;
+						break;
 					default:
 						break;
 				}