Mercurial > minori
changeset 291:9a88e1725fd2
*: refactor lots of stuff
I forgot to put this into different commits, oops!
anyway, it doesn't really matter *that* much since this is an
unfinished hobby project anyway. once it starts getting stable
commit history will be more important, but for now it's not
that big of a deal
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Sun, 12 May 2024 16:31:07 -0400 |
parents | 9347e2eaf6e5 |
children | ac1451035c85 |
files | Makefile.am configure.ac include/core/http.h include/core/session.h include/gui/pages/anime_list.h include/gui/widgets/anime_info.h include/gui/widgets/poster.h include/gui/widgets/text.h include/gui/window.h src/core/http.cc src/gui/dialog/information.cc src/gui/pages/anime_list.cc src/gui/pages/seasons.cc src/gui/pages/statistics.cc src/gui/pages/torrents.cc src/gui/theme.cc src/gui/widgets/anime_button.cc src/gui/widgets/anime_info.cc src/gui/widgets/poster.cc src/gui/widgets/sidebar.cc src/gui/widgets/text.cc src/gui/window.cc src/services/anilist.cc |
diffstat | 23 files changed, 438 insertions(+), 266 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile.am Wed May 08 17:32:28 2024 -0400 +++ b/Makefile.am Sun May 12 16:31:07 2024 -0400 @@ -20,7 +20,8 @@ minori_qtrc = \ $(top_srcdir)/rc/icons/icons.qrc \ - $(top_srcdir)/rc/animone.qrc + $(top_srcdir)/rc/animone.qrc \ + rc/locale/translations.qrc # various things we want to distribute @@ -182,6 +183,8 @@ minori_utf8proc_sources = \ dep/utf8proc/utf8proc.c +minori_moc_sources = $(minori_qtheaders:.h=_moc.cc) + minori_SOURCES = \ src/core/anime_db.cc \ src/core/anime.cc \ @@ -239,11 +242,8 @@ $(minori_locale_qm) \ $(minori_moc_sources) \ $(minori_utf8proc_sources) \ - rc/locale/translations.qrc \ rc/final_qrc.cc -minori_moc_sources = $(minori_qtheaders:.h=_moc.cc) - minori_includes = \ -I$(top_srcdir)/include \ -I$(top_srcdir)/dep/animone/include \
--- a/configure.ac Wed May 08 17:32:28 2024 -0400 +++ b/configure.ac Sun May 12 16:31:07 2024 -0400 @@ -23,49 +23,65 @@ dnl Qt? + + PKG_CHECK_MODULES([QT], [Qt5Core >= 5.7.0 Qt5Widgets Qt5Gui], [ QT_PATH="$(eval $PKG_CONFIG --variable=exec_prefix Qt5Core)" QT_HOST_PATH="$(eval $PKG_CONFIG --variable=host_bins Qt5Core)" - QT_VERSION="$(eval $PKG_CONFIG --modversion Qt5Gui)" + AC_PATH_PROGS(QT_MOC, [moc-qt5 moc], moc, ["${QT_HOST_PATH}" "${QT_PATH}/bin"]) AC_PATH_PROGS(QT_RCC, [rcc-qt5 rcc], rcc, ["${QT_HOST_PATH}" "${QT_PATH}/bin"]) AC_PATH_PROGS(QT_UIC, [uic-qt5 uic], uic, ["${QT_HOST_PATH}" "${QT_PATH}/bin"]) AC_PATH_PROGS(QT_LRELEASE, [lrelease-qt5 lrelease], lrelease, ["${QT_HOST_PATH}" "${QT_PATH}/bin"]) AC_PATH_PROGS(QT_LUPDATE, [lupdate-qt5 lupdate], lupdate, ["${QT_HOST_PATH}" "${QT_PATH}/bin"]) -], [ - AC_MSG_ERROR([${QT_PKG_ERRORS}.]) -]) -dnl On some platforms (see: Debian), Qt is built with -dnl `-reduce-relocations`, which requires applications -dnl to be built with position-independent code. -dnl -dnl Unfortunately there's no way to check for this -dnl without using qmake (bleugh), so we use this check -dnl to see if qglobal.h can be included without PIC. + dnl On some platforms (see: Debian), Qt is built with + dnl `-reduce-relocations`, which requires applications + dnl to be built with position-independent code. + dnl + dnl Unfortunately there's no way to check for this + dnl without using qmake (bleugh), so we use this check + dnl to see if qglobal.h can be included without PIC. -AC_MSG_CHECKING([for Qt requiring -fPIC]) + AC_MSG_CHECKING([for Qt requiring -fPIC]) -saved_CXXFLAGS="$CXXFLAGS" -CXXFLAGS="$CXXFLAGS $QT_CFLAGS" -AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM([#include <qglobal.h>], [])], - [ - AC_MSG_RESULT([no]) - CXXFLAGS="$saved_CXXFLAGS" - ], - [ - AC_MSG_RESULT([yes]) - CXXFLAGS="$saved_CXXFLAGS -fPIC -DPIC" - ] -) + saved_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $QT_CFLAGS" + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([#include <qglobal.h>], [])], + [ + AC_MSG_RESULT([no]) + CXXFLAGS="$saved_CXXFLAGS" + ], + [ + AC_MSG_RESULT([yes]) + CXXFLAGS="$saved_CXXFLAGS -fPIC -DPIC" + ] + ) +], [ + PKG_CHECK_MODULES([QT], [Qt6Core Qt6Widgets Qt6Gui], [ + QT_BIN_DIRECTORY="$(eval $PKG_CONFIG --variable=bindir Qt6Core)" + QT_LIBEXEC_DIRECTORY="$(eval $PKG_CONFIG --variable=libexecdir Qt6Core)" + + AC_PATH_PROGS(QT_MOC, [moc], moc, ["${QT_LIBEXEC_DIRECTORY}"]) + AC_PATH_PROGS(QT_RCC, [rcc], rcc, ["${QT_LIBEXEC_DIRECTORY}"]) + AC_PATH_PROGS(QT_UIC, [uic], uic, ["${QT_LIBEXEC_DIRECTORY}"]) + AC_PATH_PROGS(QT_LRELEASE, [lrelease], lrelease, ["${QT_BIN_DIRECTORY}"]) + AC_PATH_PROGS(QT_LUPDATE, [lupdate], lupdate, ["${QT_BIN_DIRECTORY}"]) + + dnl in my testing Qt 6 seems to require PIC unconditionally... + CXXFLAGS="$CXXFLAGS -fPIC" + ], [ + AC_MSG_ERROR([${QT_PKG_ERRORS}.]) + ]) +]) dnl need this for moc AC_PROG_MKDIR_P AC_SUBST([MKDIR_P]) dnl libcurl? -LIBCURL_CHECK_CONFIG([yes], [7.7.2], [have_libcurl=yes], [have_libcurl=no]) +LIBCURL_CHECK_CONFIG([yes], [7.87.0], [have_libcurl=yes], [have_libcurl=no]) AS_IF([test "x$have_libcurl" = "xno"], [AC_MSG_ERROR([*** libcurl not found.])])
--- a/include/core/http.h Wed May 08 17:32:28 2024 -0400 +++ b/include/core/http.h Sun May 12 16:31:07 2024 -0400 @@ -3,55 +3,54 @@ #include <QByteArray> #include <QThread> + #include <string> #include <vector> +#include <mutex> namespace HTTP { -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 = {}); +enum class Type { + Get, + Post +}; -class GetThread : public QThread { +QByteArray Request(const std::string& url, const std::vector<std::string>& headers = {}, const std::string& data = "", Type type = Type::Get); + +class RequestThread final : public QThread { Q_OBJECT public: - GetThread(const std::string& u, const std::vector<std::string>& h = {}, QObject* parent = nullptr) - : QThread(parent) { - url = u; - headers = h; - } + RequestThread(Type type = Type::Get, QObject* parent = nullptr); + RequestThread(const std::string& url, const std::vector<std::string>& headers = {}, + const std::string& data = "", Type type = Type::Get, QObject* parent = nullptr); + ~RequestThread(); + + void SetUrl(const std::string& url); + void SetHeaders(const std::vector<std::string>& headers); + void SetData(const std::string& data); + void SetType(Type type); + + void Stop(); 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 + void run() override; + static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userdata); -public: - PostThread(const std::string& u, const std::string& d, const std::vector<std::string>& h = {}, - QObject* parent = nullptr) - : QThread(parent) { - url = u; - data = d; - headers = h; - } + std::string url_; + std::string data_; + std::vector<std::string> headers_; + Type type_; -signals: - void ReceivedData(const QByteArray& ba); + /* these are passed to the write callback */ + QByteArray array_; + bool cancelled_ = false; -protected: - void run() override { emit ReceivedData(Post(url, data, headers)); } - - std::string url; - std::string data; - std::vector<std::string> headers; + /* don't fuck this up */ + std::mutex callback_data_mutex_; }; } // namespace HTTP
--- a/include/core/session.h Wed May 08 17:32:28 2024 -0400 +++ b/include/core/session.h Sun May 12 16:31:07 2024 -0400 @@ -7,21 +7,22 @@ #include "semver/semver.hpp" -class MainWindow; +#include <atomic> struct Session { public: Session() { timer.start(); } /* we literally *cannot* be lying to the user by doing this */ void IncrementRequests() { requests++; }; - int GetRequests() { return requests; }; + unsigned int GetRequests() { return requests; }; int uptime() { return timer.elapsed(); } Config config; static constexpr semver::version version{PACKAGE_VERSION}; private: - unsigned int requests = 0; + /* IncrementRequests() gets called by different threads */ + std::atomic<unsigned int> requests = 0; QElapsedTimer timer; };
--- a/include/gui/pages/anime_list.h Wed May 08 17:32:28 2024 -0400 +++ b/include/gui/pages/anime_list.h Sun May 12 16:31:07 2024 -0400 @@ -79,7 +79,7 @@ Q_OBJECT public: - AnimeListPage(QWidget* parent); + AnimeListPage(QWidget* parent = nullptr); void Refresh(); protected:
--- a/include/gui/widgets/anime_info.h Wed May 08 17:32:28 2024 -0400 +++ b/include/gui/widgets/anime_info.h Sun May 12 16:31:07 2024 -0400 @@ -2,12 +2,7 @@ #define MINORI_GUI_WIDGETS_ANIME_INFO_H_ #include <QWidget> - -namespace TextWidgets { -class OneLineSection; -class LabelledSection; -class SelectableSection; -} // namespace TextWidgets +#include "gui/widgets/text.h" namespace Anime { class Anime; @@ -22,9 +17,9 @@ void SetAnime(const Anime::Anime& anime); private: - std::shared_ptr<TextWidgets::OneLineSection> _title = nullptr; - std::shared_ptr<TextWidgets::LabelledSection> _details = nullptr; - std::shared_ptr<TextWidgets::SelectableSection> _synopsis = nullptr; + TextWidgets::OneLineSection _title; + TextWidgets::LabelledSection _details; + TextWidgets::SelectableSection _synopsis; }; #endif // MINORI_GUI_WIDGETS_ANIME_INFO_H_
--- a/include/gui/widgets/poster.h Wed May 08 17:32:28 2024 -0400 +++ b/include/gui/widgets/poster.h Sun May 12 16:31:07 2024 -0400 @@ -4,6 +4,7 @@ #include <QImage> #include "gui/widgets/clickable_label.h" +#include "core/http.h" class QWidget; namespace Anime { @@ -25,6 +26,8 @@ void RenderToLabel(); private: + HTTP::RequestThread get_thread_; + QImage img_; QString service_url_; ClickableLabel label_;
--- a/include/gui/widgets/text.h Wed May 08 17:32:28 2024 -0400 +++ b/include/gui/widgets/text.h Sun May 12 16:31:07 2024 -0400 @@ -21,16 +21,24 @@ void SetText(const QString& title); private: - QLabel* static_text_title; - QFrame* static_text_line; + QLabel static_text_title; + QFrame static_text_line; }; -class Paragraph : public QLabel { +class Paragraph : public QWidget { Q_OBJECT public: Paragraph(const QString& text, QWidget* parent = nullptr); void SetText(const QString& text); + QPlainTextEdit* GetLabel(); + +protected: + QSize minimumSizeHint() const; + QSize sizeHint() const; + +private: + QPlainTextEdit text_edit; }; class LabelledParagraph final : public QWidget { @@ -38,21 +46,27 @@ public: LabelledParagraph(const QString& label, const QString& data, QWidget* parent = nullptr); - Paragraph* GetLabels(); - Paragraph* GetParagraph(); + QLabel* GetLabels(); + QLabel* GetData(); + + /* synonymous with GetData(), kept for compatibility. don't use in new code!!! */ + QLabel* GetParagraph(); private: - Paragraph* labels; - Paragraph* paragraph; + QLabel labels_; + QLabel data_; }; -class Line : public Paragraph { +class Line : public QWidget { Q_OBJECT public: Line(QWidget* parent = nullptr); Line(const QString& text, QWidget* parent = nullptr); void SetText(const QString& text); + +protected: + QLineEdit line_edit_; }; class Title final : public Line { @@ -81,8 +95,9 @@ public: LabelledSection(const QString& title, const QString& label, const QString& data, QWidget* parent = nullptr); Header* GetHeader(); - Paragraph* GetLabels(); - Paragraph* GetParagraph(); + QLabel* GetLabels(); + QLabel* GetData(); + QLabel* GetParagraph(); private: Header* header;
--- a/include/gui/window.h Wed May 08 17:32:28 2024 -0400 +++ b/include/gui/window.h Sun May 12 16:31:07 2024 -0400 @@ -1,22 +1,31 @@ #ifndef MINORI_WINDOW_H_ #define MINORI_WINDOW_H_ + #include "core/config.h" -#include <QMainWindow> -#include <memory> +#include "gui/widgets/sidebar.h" -/* *could* be forward-declared, but this causes - any file that #includes this to have to #include - these as well due to unique_ptr */ -#include "gui/widgets/sidebar.h" +/* pages; these should really be in a namespace */ +#include "gui/pages/anime_list.h" +#include "gui/pages/history.h" +#include "gui/pages/now_playing.h" +#include "gui/pages/search.h" +#include "gui/pages/seasons.h" +#include "gui/pages/statistics.h" +#include "gui/pages/torrents.h" + +#include <QMainWindow> #include <QCloseEvent> #include <QStackedWidget> #include <QThread> #include <QTimer> #include <QWidget> +#include <memory> + class QMenu; class AnimeListPage; +/* ... :) */ Q_DECLARE_METATYPE(std::vector<std::string>); class MainWindowPlayingThread final : public QThread { @@ -80,6 +89,14 @@ QStackedWidget stack_; SideBar sidebar_; + AnimeListPage anime_list_page_; + HistoryPage history_page_; + NowPlayingPage now_playing_page_; + SearchPage search_page_; + SeasonsPage seasons_page_; + StatisticsPage statistics_page_; + TorrentsPage torrents_page_; + MainWindowPlayingThread playing_thread_; QTimer playing_thread_timer_;
--- a/src/core/http.cc Wed May 08 17:32:28 2024 -0400 +++ b/src/core/http.cc Sun May 12 16:31:07 2024 -0400 @@ -13,16 +13,18 @@ return size * nmemb; } -QByteArray Get(const std::string& url, const std::vector<std::string>& headers) { +QByteArray Request(const std::string& url, const std::vector<std::string>& headers, const std::string& data, Type type) { struct curl_slist* list = NULL; QByteArray userdata; CURL* curl = curl_easy_init(); if (curl) { - for (const std::string& h : headers) { + for (const std::string& h : headers) list = curl_slist_append(list, h.c_str()); - } + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + if (type == Type::Post) + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &userdata); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &WriteCallback); @@ -38,30 +40,99 @@ return userdata; } -QByteArray Post(const std::string& url, const std::string& data, const std::vector<std::string>& headers) { +/* this function is static */ +size_t RequestThread::WriteCallback(void* contents, size_t size, size_t nmemb, void* userdata) { + RequestThread* thread = reinterpret_cast<RequestThread*>(userdata); + + const std::lock_guard<std::mutex> lock(thread->callback_data_mutex_); + + /* stop writing, then! */ + if (thread->cancelled_) + return CURL_WRITEFUNC_ERROR; + + /* else, continue on as normal */ + thread->array_.append(reinterpret_cast<char*>(contents), size * nmemb); + return size * nmemb; +} + +RequestThread::RequestThread(Type type, QObject* parent) : QThread(parent) { + SetType(type); +} + +RequestThread::RequestThread(const std::string& url, const std::vector<std::string>& headers, + const std::string& data, Type type, QObject* parent) + : QThread(parent) { + SetUrl(url); + SetData(data); + SetHeaders(headers); + SetType(type); +} + +RequestThread::~RequestThread() { + /* block until the function can safely exit. + * + * this sucks. find out a better way to do this, which will probably + * be to put all of the threads in a pool */ + Stop(); + wait(); +} + +void RequestThread::SetUrl(const std::string& url) { + url_ = url; +} + +void RequestThread::SetHeaders(const std::vector<std::string>& headers) { + headers_ = headers; +} + +void RequestThread::SetData(const std::string& data) { + data_ = data; +} + +void RequestThread::SetType(Type type) { + type_ = type; +} + +void RequestThread::run() { struct curl_slist* list = NULL; - QByteArray userdata; CURL* curl = curl_easy_init(); if (curl) { - for (const std::string& h : headers) { + curl_easy_setopt(curl, CURLOPT_URL, url_.c_str()); + + if (type_ == Type::Post) + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data_.c_str()); + + for (const std::string& h : headers_) list = curl_slist_append(list, h.c_str()); - } - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &userdata); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &WriteCallback); + + curl_easy_setopt(curl, CURLOPT_WRITEDATA, this); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &RequestThread::WriteCallback); + /* Use system certs... useful on Windows. */ curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); // threading + + /* does something with threading, don't remember what though */ + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + CURLcode res = curl_easy_perform(curl); session.IncrementRequests(); curl_easy_cleanup(curl); - if (res != CURLE_OK) + + callback_data_mutex_.lock(); + if (res != CURLE_OK && !(res == CURLE_WRITE_ERROR && cancelled_)) std::cerr << "curl_easy_perform(curl) failed!: " << curl_easy_strerror(res) << std::endl; + callback_data_mutex_.unlock(); } - return userdata; + + emit ReceivedData(array_); + array_.clear(); +} + +void RequestThread::Stop() { + const std::lock_guard<std::mutex> lock(callback_data_mutex_); + cancelled_ = true; } } // namespace HTTP
--- a/src/gui/dialog/information.cc Wed May 08 17:32:28 2024 -0400 +++ b/src/gui/dialog/information.cc Sun May 12 16:31:07 2024 -0400 @@ -171,7 +171,7 @@ QComboBox* combo_box = new QComboBox(section); for (unsigned int i = 0; i < Anime::ListStatuses.size(); i++) - combo_box->addItem(Strings::ToQString(Translate::ToString(Anime::ListStatuses[i])), + combo_box->addItem(Strings::ToQString(Translate::ToLocalString(Anime::ListStatuses[i])), static_cast<int>(Anime::ListStatuses[i])); connect(combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
--- a/src/gui/pages/anime_list.cc Wed May 08 17:32:28 2024 -0400 +++ b/src/gui/pages/anime_list.cc Sun May 12 16:31:07 2024 -0400 @@ -320,6 +320,7 @@ dialog->show(); dialog->raise(); dialog->activateWindow(); + connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater); } }); menu->addSeparator(); @@ -331,6 +332,7 @@ dialog->show(); dialog->raise(); dialog->activateWindow(); + connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater); } }); menu->addAction(tr("Delete from list..."), [this, animes] { @@ -361,6 +363,7 @@ dialog->show(); dialog->raise(); dialog->activateWindow(); + connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater); } void AnimeListPage::RefreshList() {
--- a/src/gui/pages/seasons.cc Wed May 08 17:32:28 2024 -0400 +++ b/src/gui/pages/seasons.cc Sun May 12 16:31:07 2024 -0400 @@ -48,7 +48,7 @@ toolbar->setMovable(false); { - /* hard-coded this value */ + /* todo: clean this up... this sucks... */ static constexpr Date::Year last_year = 1960; auto create_year_menu = [this](QWidget* parent, QMenu* parent_menu, Date::Year year){ @@ -72,12 +72,12 @@ }; /* we'll be extinct by the time this code breaks, so I guess it's fine :) */ - const Date::Year year = static_cast<Date::Year>(QDate::currentDate().year()); - const Date::Year year_before_collapse = GetClosestDecade(year) - 10; + const Date::Year current_year = static_cast<Date::Year>(QDate::currentDate().year()); + const Date::Year year_before_collapse = GetClosestDecade(current_year) - 10; season_button = new QToolButton(toolbar); QMenu* full_season_menu = new QMenu(season_button); - for (Date::Year c = year; c >= year_before_collapse; c--) + for (Date::Year c = current_year; c >= year_before_collapse; c--) create_year_menu(season_button, full_season_menu, c); full_season_menu->addSeparator(); @@ -103,15 +103,15 @@ { /* links */ QMenu* menu = new QMenu(button); - menu->addAction("Airing status"); - menu->addAction("List status"); - menu->addAction("Type"); + menu->addAction(tr("Airing status")); + menu->addAction(tr("List status")); + menu->addAction(tr("Type")); button->setMenu(menu); } button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); button->setIcon(QIcon(":/icons/16x16/category.png")); - button->setText("Group by:"); + button->setText(tr("Group by:")); button->setPopupMode(QToolButton::InstantPopup); toolbar->addWidget(button); } @@ -122,17 +122,17 @@ { /* links */ QMenu* menu = new QMenu(button); - menu->addAction("Airing date"); - menu->addAction("Episodes"); - menu->addAction("Popularity"); - menu->addAction("Score"); - menu->addAction("Title"); + menu->addAction(tr("Airing date")); + menu->addAction(tr("Episodes")); + menu->addAction(tr("Popularity")); + menu->addAction(tr("Score")); + menu->addAction(tr("Title")); button->setMenu(menu); } button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); button->setIcon(QIcon(":/icons/16x16/sort-quantity-descending.png")); - button->setText("Sort by:"); + button->setText(tr("Sort by:")); button->setPopupMode(QToolButton::InstantPopup); toolbar->addWidget(button); } @@ -143,14 +143,14 @@ { /* links */ QMenu* menu = new QMenu(button); - menu->addAction("Images"); - menu->addAction("Details"); + menu->addAction(tr("Images")); + menu->addAction(tr("Details")); button->setMenu(menu); } button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); button->setIcon(QIcon(":/icons/16x16/ui-scroll-pane-detail.png")); - button->setText("View:"); + button->setText(tr("View:")); button->setPopupMode(QToolButton::InstantPopup); toolbar->addWidget(button); }
--- a/src/gui/pages/statistics.cc Wed May 08 17:32:28 2024 -0400 +++ b/src/gui/pages/statistics.cc Sun May 12 16:31:07 2024 -0400 @@ -141,7 +141,7 @@ ts << Strings::ToQString(TimeToDateString(TimeUnits::MINUTES, Anime::db.GetTotalPlannedAmount(), 60.0)) << '\n'; ts << Anime::db.GetAverageScore() << '\n'; ts << Anime::db.GetScoreDeviation(); - _anime_list->GetParagraph()->SetText(string); + _anime_list->GetData()->setText(string); _score_distribution_graph->Clear(); for (int i = 10; i <= 100; i += 10) @@ -152,5 +152,5 @@ ts << session.GetRequests(); /* Application */ // UiUtils::SetPlainTextEditData(application_data, QString::number(session.uptime() / 1000)); - _application->GetParagraph()->SetText(string); + _application->GetData()->setText(string); }
--- a/src/gui/pages/torrents.cc Wed May 08 17:32:28 2024 -0400 +++ b/src/gui/pages/torrents.cc Sun May 12 16:31:07 2024 -0400 @@ -65,9 +65,10 @@ const std::filesystem::path torrents_dir = Filesystem::GetTorrentsPath(); std::filesystem::create_directories(torrents_dir); - HTTP::GetThread* thread = new HTTP::GetThread(link, {}, this); + /* this sucks */ + HTTP::RequestThread* thread = new HTTP::RequestThread(link, {}, "", HTTP::Type::Get, this); - connect(thread, &HTTP::GetThread::ReceivedData, this, [this, torrents_dir, filename](const QByteArray& data) { + connect(thread, &HTTP::RequestThread::ReceivedData, this, [this, torrents_dir, filename](const QByteArray& data) { std::ofstream file(torrents_dir / filename, std::ofstream::out | std::ofstream::trunc); if (!file) return; // wat @@ -75,14 +76,14 @@ file.write(data.data(), data.size()); file.close(); }); - connect(thread, &HTTP::GetThread::finished, thread, &HTTP::GetThread::deleteLater); + connect(thread, &HTTP::RequestThread::finished, thread, &HTTP::RequestThread::deleteLater); thread->start(); } } QByteArray TorrentsPageListModel::DownloadTorrentList() { - return HTTP::Get(session.config.torrents.feed_link); + return HTTP::Request(session.config.torrents.feed_link); } void TorrentsPageListModel::ParseFeedDescription(const std::string& description, Torrent& torrent) { @@ -416,9 +417,9 @@ if (!model) return; - HTTP::GetThread* thread = new HTTP::GetThread(session.config.torrents.feed_link); + HTTP::RequestThread* thread = new HTTP::RequestThread(session.config.torrents.feed_link); - connect(thread, &HTTP::GetThread::ReceivedData, this, [&](const QByteArray& ba) { + connect(thread, &HTTP::RequestThread::ReceivedData, this, [&](const QByteArray& ba) { /* This is to make sure we aren't in a different thread * messing around with GUI stuff */ @@ -426,7 +427,7 @@ model->ParseTorrentList(ba); treeview->setUpdatesEnabled(true); }); - connect(thread, &QThread::finished, thread, &QThread::deleteLater); + connect(thread, &HTTP::RequestThread::finished, thread, &HTTP::RequestThread::deleteLater); thread->start(); }
--- a/src/gui/theme.cc Wed May 08 17:32:28 2024 -0400 +++ b/src/gui/theme.cc Sun May 12 16:31:07 2024 -0400 @@ -7,6 +7,7 @@ #include <QStyleFactory> #include <QTextStream> #include <QStyle> +#include <QtGlobal> #ifdef MACOSX # include "sys/osx/dark_theme.h" #elif defined(WIN32)
--- a/src/gui/widgets/anime_button.cc Wed May 08 17:32:28 2024 -0400 +++ b/src/gui/widgets/anime_button.cc Sun May 12 16:31:07 2024 -0400 @@ -91,10 +91,10 @@ { const QLocale& locale = session.config.locale.GetLocale(); - _info.GetParagraph()->SetText(locale.toString(anime.GetAirDate().GetAsQDate(), "dd MMM yyyy") + "\n" + - QString::number(anime.GetEpisodes()) + "\n" + - Strings::ToQString(Strings::Implode(anime.GetGenres(), ", ")) + "\n" + "...\n" + - QString::number(anime.GetAudienceScore()) + "%\n" + "..."); + _info.GetData()->setText(locale.toString(anime.GetAirDate().GetAsQDate(), "dd MMM yyyy") + "\n" + + QString::number(anime.GetEpisodes()) + "\n" + + Strings::ToQString(Strings::Implode(anime.GetGenres(), ", ")) + "\n" + "...\n" + + QString::number(anime.GetAudienceScore()) + "%\n" + "..."); } _synopsis.SetText(Strings::ToQString(anime.GetSynopsis()));
--- a/src/gui/widgets/anime_info.cc Wed May 08 17:32:28 2024 -0400 +++ b/src/gui/widgets/anime_info.cc Sun May 12 16:31:07 2024 -0400 @@ -6,21 +6,16 @@ #include <QHBoxLayout> #include <QTextStream> -AnimeInfoWidget::AnimeInfoWidget(QWidget* parent) : QWidget(parent) { +AnimeInfoWidget::AnimeInfoWidget(QWidget* parent) + : QWidget(parent) + , _title(tr("Alternative titles"), "") + , _details(tr("Details"), tr("Type:\nEpisodes:\nStatus:\nSeason:\nGenres:\nScore:"), "") + , _synopsis(tr("Synopsis"), "") { QVBoxLayout* layout = new QVBoxLayout(this); - _title.reset(new TextWidgets::OneLineSection(tr("Alternative titles"), "", this)); - layout->addWidget(_title.get()); - - _details.reset(new TextWidgets::LabelledSection( - tr("Details"), tr("Type:\nEpisodes:\nStatus:\nSeason:\nGenres:\nScore:"), "", this)); - layout->addWidget(_details.get()); - - _synopsis.reset(new TextWidgets::SelectableSection(tr("Synopsis"), "", this)); - _synopsis->GetParagraph()->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); - layout->addWidget(_synopsis.get()); - - layout->addStretch(); + layout->addWidget(&_title); + layout->addWidget(&_details); + layout->addWidget(&_synopsis); } AnimeInfoWidget::AnimeInfoWidget(const Anime::Anime& anime, QWidget* parent) : AnimeInfoWidget(parent) { @@ -29,15 +24,15 @@ void AnimeInfoWidget::SetAnime(const Anime::Anime& anime) { /* alt titles */ - _title->GetLine()->SetText(Strings::ToQString(Strings::Implode(anime.GetTitleSynonyms(), ", "))); + _title.GetLine()->SetText(Strings::ToQString(Strings::Implode(anime.GetTitleSynonyms(), ", "))); /* details */ QString details_data; QTextStream details_data_s(&details_data); + /* we have to convert ALL of these strings to * QString because QTextStream sucks and assumes - * Latin1 (on Windows?) - */ + * Latin1 (on Windows?) */ const auto genres = anime.GetGenres(); details_data_s << Strings::ToQString(Translate::ToLocalString(anime.GetFormat())) << "\n" << anime.GetEpisodes() << "\n" @@ -46,9 +41,9 @@ << anime.GetAirDate().GetYear().value_or(2000) << "\n" << Strings::ToQString((genres.size() > 1) ? Strings::Implode(genres, ", ") : "-") << "\n" << anime.GetAudienceScore() << "%"; - _details->GetParagraph()->SetText(details_data); + _details.GetData()->setText(details_data); - _synopsis->GetParagraph()->SetText(Strings::ToQString(anime.GetSynopsis())); + _synopsis.GetParagraph()->SetText(Strings::ToQString(anime.GetSynopsis())); updateGeometry(); }
--- a/src/gui/widgets/poster.cc Wed May 08 17:32:28 2024 -0400 +++ b/src/gui/widgets/poster.cc Sun May 12 16:31:07 2024 -0400 @@ -27,6 +27,8 @@ label_.setAlignment(Qt::AlignCenter); layout->addWidget(&label_); + + connect(&get_thread_, &HTTP::RequestThread::ReceivedData, this, &Poster::ImageDownloadFinished); } Poster::Poster(const Anime::Anime& anime, QWidget* parent) : Poster(parent) { @@ -35,14 +37,12 @@ void Poster::SetAnime(const Anime::Anime& anime) { /* todo: only download on showEvent() */ - { - HTTP::GetThread* thread = new HTTP::GetThread(anime.GetPosterUrl(), {}, this); + if (get_thread_.isRunning()) + get_thread_.Stop(); + get_thread_.wait(); - connect(thread, &HTTP::GetThread::ReceivedData, this, &Poster::ImageDownloadFinished); - connect(thread, &HTTP::GetThread::finished, thread, &HTTP::GetThread::deleteLater); - - thread->start(); - } + get_thread_.SetUrl(anime.GetPosterUrl()); + get_thread_.start(); std::optional<std::string> url = anime.GetServiceUrl(session.config.service); if (url)
--- a/src/gui/widgets/sidebar.cc Wed May 08 17:32:28 2024 -0400 +++ b/src/gui/widgets/sidebar.cc Sun May 12 16:31:07 2024 -0400 @@ -30,10 +30,6 @@ void SideBar::SetBackgroundColor(QColor color) { viewport()->setAutoFillBackground(color != Qt::transparent); - - QPalette pal(palette()); - pal.setColor(QPalette::Window, color); - setPalette(pal); } QListWidgetItem* SideBar::AddItem(QString name, QIcon icon) {
--- a/src/gui/widgets/text.cc Wed May 08 17:32:28 2024 -0400 +++ b/src/gui/widgets/text.cc Sun May 12 16:31:07 2024 -0400 @@ -6,82 +6,130 @@ #include <QLabel> #include <QTextBlock> #include <QVBoxLayout> +#include <QScrollArea> +#include <QDebug> + +/* WARNING: GARBAGE CODE FOLLOWS + * + * This file is filled with spaghetti to make this + * stupid text render how I want it to. + * + * many cases of hacking with setSizePolicy() are seen + * all around this file. Edit it only if really + * necessary, please. +*/ namespace TextWidgets { -Header::Header(const QString& title, QWidget* parent) : QWidget(parent) { +Header::Header(const QString& title, QWidget* parent) + : QWidget(parent) + , static_text_title(title) { QVBoxLayout* layout = new QVBoxLayout(this); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); - static_text_title = new QLabel(title, this); - static_text_title->setTextFormat(Qt::PlainText); + static_text_title.setTextFormat(Qt::PlainText); { - QFont font = static_text_title->font(); + QFont font = static_text_title.font(); font.setWeight(QFont::Bold); - static_text_title->setFont(font); + static_text_title.setFont(font); } - static_text_line = new QFrame(this); - static_text_line->setFrameShape(QFrame::HLine); - static_text_line->setFrameShadow(QFrame::Sunken); - static_text_line->setFixedHeight(2); + static_text_line.setFrameShape(QFrame::HLine); + static_text_line.setFrameShadow(QFrame::Sunken); + static_text_line.setFixedHeight(2); - layout->addWidget(static_text_title); - layout->addWidget(static_text_line); + layout->addWidget(&static_text_title); + layout->addWidget(&static_text_line); layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); } void Header::SetText(const QString& text) { - static_text_title->setText(text); + static_text_title.setText(text); updateGeometry(); } -/* for now, this is a QLabel with a couple of default settings. - * - * eventually I'll have to implement this as a QScrollArea, just in case - * some random text decides to overflow or something. - */ -Paragraph::Paragraph(const QString& text, QWidget* parent) : QLabel(text, parent) { - setTextInteractionFlags(Qt::TextBrowserInteraction); - setFrameShape(QFrame::NoFrame); - setCursor(Qt::IBeamCursor); /* emulate Taiga */ - setWordWrap(true); +Paragraph::Paragraph(const QString& text, QWidget* parent) : QWidget(parent) { + /* meh */ + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + + text_edit.setTextInteractionFlags(Qt::TextBrowserInteraction); + text_edit.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + text_edit.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + text_edit.setStyleSheet("background: transparent;"); + + text_edit.document()->setDocumentMargin(0); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + SetText(text); + + layout->addWidget(&text_edit); +} + +void Paragraph::SetText(const QString& text) { + text_edit.document()->setPlainText(text); + + /* return the view to the start */ + QTextCursor cursor = text_edit.textCursor(); + cursor.setPosition(0); + text_edit.setTextCursor(cursor); +} + +QSize Paragraph::minimumSizeHint() const { + return QSize(0, 0); } -/* kept here for legacy reasons, see explanation above */ -void Paragraph::SetText(const QString& text) { - setText(text); +/* highly based upon... some stackoverflow answer for PyQt */ +QSize Paragraph::sizeHint() const { + QTextDocument* doc = text_edit.document(); + doc->adjustSize(); + + long h = 0; + for (QTextBlock line = doc->begin(); line != doc->end(); line = line.next()) + h += doc->documentLayout()->blockBoundingRect(line).height(); + + return QSize(doc->size().width(), h); +} + +QPlainTextEdit* Paragraph::GetLabel() { + return &text_edit; } -/* Equivalent to Paragraph(), but disables word wrap. */ -Line::Line(QWidget* parent) : Paragraph("", parent) { - setWordWrap(false); +Line::Line(QWidget* parent) : QWidget(parent) { + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + + line_edit_.setReadOnly(true); + line_edit_.setFrame(false); + line_edit_.setStyleSheet("background: transparent;"); + + layout->addWidget(&line_edit_); } -Line::Line(const QString& text, QWidget* parent) : Paragraph(text, parent) { - setWordWrap(false); +Line::Line(const QString& text, QWidget* parent) : Line(parent) { + SetText(text); } -/* legacy function, don't use in new code */ void Line::SetText(const QString& text) { - setText(text); + line_edit_.setText(text); + line_edit_.setCursorPosition(0); } Title::Title(const QString& title, QWidget* parent) : Line(title, parent) { - QFont fnt(font()); + QFont fnt(line_edit_.font()); fnt.setPixelSize(16); - setFont(fnt); + line_edit_.setFont(fnt); - QPalette pal(palette()); - pal.setColor(QPalette::Text, pal.color(QPalette::Highlight)); - setPalette(pal); + line_edit_.setForegroundRole(QPalette::Highlight); } -Section::Section(const QString& title, const QString& data, QWidget* parent) : QWidget(parent) { +Section::Section(const QString& title, const QString& data, QWidget* parent) + : QWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(this); header = new Header(title, this); @@ -90,9 +138,9 @@ QHBoxLayout* content_layout = new QHBoxLayout(content); paragraph = new Paragraph(data, this); - paragraph->setTextInteractionFlags(Qt::NoTextInteraction); - paragraph->setAttribute(Qt::WidgetAttribute::WA_TransparentForMouseEvents); - paragraph->setWordWrap(QTextOption::NoWrap); + paragraph->GetLabel()->setTextInteractionFlags(Qt::NoTextInteraction); + paragraph->GetLabel()->setAttribute(Qt::WidgetAttribute::WA_TransparentForMouseEvents); + paragraph->GetLabel()->setWordWrapMode(QTextOption::NoWrap); content_layout->addWidget(paragraph); content_layout->setSpacing(0); @@ -113,33 +161,32 @@ return paragraph; } -LabelledParagraph::LabelledParagraph(const QString& label, const QString& data, QWidget* parent) : QWidget(parent) { +/* despite being named a "labelled paragraph" this uses QLabels for simplicity */ +LabelledParagraph::LabelledParagraph(const QString& label, const QString& data, QWidget* parent) + : QWidget(parent) + , labels_(label) + , data_(data) { QHBoxLayout* ly = new QHBoxLayout(this); - labels = new Paragraph(label, this); - labels->setTextInteractionFlags(Qt::NoTextInteraction); - labels->setAttribute(Qt::WidgetAttribute::WA_TransparentForMouseEvents); - labels->setWordWrap(QTextOption::NoWrap); - labels->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding); + labels_.setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + data_.setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); - paragraph = new Paragraph(data, this); - paragraph->setTextInteractionFlags(Qt::NoTextInteraction); - paragraph->setAttribute(Qt::WidgetAttribute::WA_TransparentForMouseEvents); - paragraph->setWordWrap(QTextOption::NoWrap); - paragraph->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - - ly->addWidget(labels, 0, Qt::AlignTop); - ly->addWidget(paragraph, 0, Qt::AlignTop); + ly->addWidget(&labels_, 0, Qt::AlignTop); + ly->addWidget(&data_, 0, Qt::AlignTop); ly->setSpacing(20); ly->setContentsMargins(0, 0, 0, 0); } -Paragraph* LabelledParagraph::GetLabels() { - return labels; +QLabel* LabelledParagraph::GetLabels() { + return &labels_; } -Paragraph* LabelledParagraph::GetParagraph() { - return paragraph; +QLabel* LabelledParagraph::GetData() { + return &data_; +} + +QLabel* LabelledParagraph::GetParagraph() { + return GetData(); } LabelledSection::LabelledSection(const QString& title, const QString& label, const QString& data, QWidget* parent) @@ -151,7 +198,7 @@ // this is not accessible from the object because there's really // no reason to make it accessible... content = new LabelledParagraph(label, data, this); - content->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); + content->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); content->setContentsMargins(12, 0, 0, 0); layout->addWidget(header); @@ -164,11 +211,15 @@ return header; } -Paragraph* LabelledSection::GetLabels() { +QLabel* LabelledSection::GetLabels() { return content->GetLabels(); } -Paragraph* LabelledSection::GetParagraph() { +QLabel* LabelledSection::GetData() { + return content->GetData(); +} + +QLabel* LabelledSection::GetParagraph() { return content->GetParagraph(); }
--- a/src/gui/window.cc Wed May 08 17:32:28 2024 -0400 +++ b/src/gui/window.cc Sun May 12 16:31:07 2024 -0400 @@ -74,21 +74,32 @@ action_->setEnabled(true); } -MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), async_synchronize_thread_(nullptr, nullptr) { +MainWindow::MainWindow(QWidget* parent) + : QMainWindow(parent) + , async_synchronize_thread_(nullptr, nullptr) { setWindowIcon(QIcon(":/icons/favicon.png")); sidebar_.setFixedWidth(128); sidebar_.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); - connect(&sidebar_, &SideBar::CurrentItemChanged, &stack_, &QStackedWidget::setCurrentIndex); - new QHBoxLayout(&main_widget_); + CreateBars(); + + stack_.addWidget(&now_playing_page_); + /* ---- */ + stack_.addWidget(&anime_list_page_); + stack_.addWidget(&history_page_); + stack_.addWidget(&statistics_page_); + /* ---- */ + stack_.addWidget(&search_page_); + stack_.addWidget(&seasons_page_); + stack_.addWidget(&torrents_page_); + AddMainWidgets(); + sidebar_.SetCurrentItem(static_cast<int>(Pages::ANIME_LIST)); setCentralWidget(&main_widget_); - CreateBars(); - NowPlayingPage* page = reinterpret_cast<NowPlayingPage*>(stack_.widget(static_cast<int>(Pages::NOW_PLAYING))); connect(&playing_thread_, &MainWindowPlayingThread::Done, this, [page](const std::vector<std::string>& files) { @@ -123,8 +134,9 @@ playing_thread_timer_.start(5000); } +/* Does the main part of what Qt's generic "RetranslateUI" function would do */ void MainWindow::AddMainWidgets() { - int page = static_cast<int>(Pages::ANIME_LIST); + int page = sidebar_.GetCurrentItem(); sidebar_.clear(); @@ -138,20 +150,6 @@ sidebar_.AddItem(tr("Seasons"), SideBar::CreateIcon(":/icons/16x16/calendar.png")); sidebar_.AddItem(tr("Torrents"), SideBar::CreateIcon(":/icons/16x16/feed.png")); - while (stack_.count()) - stack_.removeWidget(stack_.widget(0)); - - /* can we allocate these on the stack? */ - stack_.addWidget(new NowPlayingPage(&main_widget_)); - /* ---- */ - stack_.addWidget(new AnimeListPage(&main_widget_)); - stack_.addWidget(new HistoryPage(&main_widget_)); - stack_.addWidget(new StatisticsPage(&main_widget_)); - /* ---- */ - stack_.addWidget(new SearchPage(&main_widget_)); - stack_.addWidget(new SeasonsPage(&main_widget_)); - stack_.addWidget(new TorrentsPage(&main_widget_)); - sidebar_.SetCurrentItem(page); main_widget_.layout()->addWidget(&sidebar_); @@ -334,6 +332,8 @@ } /* pain in the ass */ + disconnect(&sidebar_, &SideBar::CurrentItemChanged, nullptr, nullptr); + connect(&sidebar_, &SideBar::CurrentItemChanged, &stack_, &QStackedWidget::setCurrentIndex); connect(&sidebar_, &SideBar::CurrentItemChanged, this, [pages_group](int index) { QAction* checked = pages_group->checkedAction(); @@ -508,8 +508,7 @@ } void MainWindow::RetranslateUI() { - /* This kinda sucks but nobody's really going to be changing - the application language all the time :p */ + /* This sucks a LOT */ setUpdatesEnabled(false); AddMainWidgets(); CreateBars(); @@ -532,14 +531,18 @@ QMainWindow::showEvent(event); #ifdef WIN32 /* Technically this *should* be - session.config.theme.IsInDarkTheme() && win32::IsInDarkTheme() - but I prefer the title bar being black even when light mode - is enabled :/ */ + * session.config.theme.IsInDarkTheme() && win32::IsInDarkTheme() + * but I prefer the title bar being black even when light mode + * is enabled :/ */ win32::SetTitleBarsToBlack(session.config.theme.IsInDarkTheme()); #endif } void MainWindow::closeEvent(QCloseEvent* event) { + playing_thread_timer_.stop(); + playing_thread_.wait(); + async_synchronize_thread_.wait(); + session.config.Save(); Anime::db.SaveDatabaseToDisk(); event->accept();
--- a/src/services/anilist.cc Wed May 08 17:32:28 2024 -0400 +++ b/src/services/anilist.cc Sun May 12 16:31:07 2024 -0400 @@ -18,6 +18,7 @@ #include <chrono> #include <exception> +#include <string_view> #include <iostream> @@ -26,7 +27,7 @@ namespace Services { namespace AniList { -static constexpr int CLIENT_ID = 13706; +static constexpr std::string_view CLIENT_ID = "13706"; class Account { public: @@ -42,13 +43,13 @@ static Account account; -std::string SendRequest(std::string data) { +static std::string SendRequest(const std::string& data) { std::vector<std::string> headers = {"Authorization: Bearer " + account.AuthToken(), "Accept: application/json", "Content-Type: application/json"}; - return Strings::ToUtf8String(HTTP::Post("https://graphql.anilist.co", data, headers)); + return Strings::ToUtf8String(HTTP::Request("https://graphql.anilist.co", headers, data, HTTP::Type::Post)); } -nlohmann::json SendJSONRequest(nlohmann::json data) { +static nlohmann::json SendJSONRequest(const nlohmann::json& data) { std::string request = SendRequest(data.dump()); if (request.empty()) { std::cerr << "[AniList] JSON Request returned an empty result!" << std::endl; @@ -72,7 +73,7 @@ return ret; } -void ParseListStatus(std::string status, Anime::Anime& anime) { +static void ParseListStatus(std::string status, Anime::Anime& anime) { static const std::unordered_map<std::string, Anime::ListStatus> map = { {"CURRENT", Anime::ListStatus::Current }, {"PLANNING", Anime::ListStatus::Planning }, @@ -95,7 +96,7 @@ anime.SetUserStatus(map.at(status)); } -std::string ListStatusToString(const Anime::Anime& anime) { +static std::string ListStatusToString(const Anime::Anime& anime) { if (anime.GetUserIsRewatching() && anime.GetUserStatus() == Anime::ListStatus::Current) return "REWATCHING"; @@ -109,7 +110,7 @@ return "CURRENT"; } -void ParseTitle(const nlohmann::json& json, Anime::Anime& anime) { +static void ParseTitle(const nlohmann::json& json, Anime::Anime& anime) { nlohmann::json::json_pointer g = "/native"_json_pointer; if (json.contains(g) && json[g].is_string()) anime.SetTitle(Anime::TitleLanguage::Native, json[g]); @@ -123,7 +124,7 @@ anime.SetTitle(Anime::TitleLanguage::Romaji, json[g]); } -int ParseMediaJson(const nlohmann::json& json) { +static int ParseMediaJson(const nlohmann::json& json) { int id = JSON::GetNumber(json, "/id"_json_pointer); if (!id) return 0; @@ -159,7 +160,7 @@ return id; } -int ParseListItem(const nlohmann::json& json) { +static int ParseListItem(const nlohmann::json& json) { int id = ParseMediaJson(json["media"]); Anime::Anime& anime = Anime::db.items[id]; @@ -179,7 +180,7 @@ return id; } -int ParseList(const nlohmann::json& json) { +static int ParseList(const nlohmann::json& json) { for (const auto& entry : json["entries"].items()) { ParseListItem(entry.value()); } @@ -336,6 +337,10 @@ if (!anime.IsInUserList()) return 0; + std::optional<std::string> service_id = anime.GetServiceId(Anime::Service::AniList); + if (!service_id) + return 0; + constexpr std::string_view query = "mutation ($media_id: Int, $progress: Int, $status: MediaListStatus, $score: Int, $notes: String, $start: " "FuzzyDateInput, $comp: FuzzyDateInput, $repeat: Int) {\n" @@ -348,7 +353,7 @@ nlohmann::json json = { {"query", query}, {"variables", { - {"media_id", anime.GetId()}, + {"media_id", Strings::ToInt<int64_t>(service_id.value())}, {"progress", anime.GetUserProgress()}, {"status", ListStatusToString(anime)}, {"score", anime.GetUserScore()}, @@ -365,7 +370,7 @@ return JSON::GetNumber(ret, "/data/SaveMediaListEntry/id"_json_pointer, 0); } -int ParseUser(const nlohmann::json& json) { +static int ParseUser(const nlohmann::json& json) { account.SetUserId(JSON::GetNumber(json, "/id"_json_pointer, 0)); return account.UserId(); } @@ -373,7 +378,7 @@ bool AuthorizeUser() { /* Prompt for PIN */ QDesktopServices::openUrl(QUrl(Strings::ToQString("https://anilist.co/api/v2/oauth/authorize?client_id=" + - Strings::ToUtf8String(CLIENT_ID) + "&response_type=token"))); + std::string(CLIENT_ID) + "&response_type=token"))); bool ok; QString token = QInputDialog::getText(