# HG changeset patch
# User Paper
# Date 1699362222 18000
# Node ID ab191e28e69d90fe880e39ce360e9d8f4f97d995
# Parent 32afe0e940bf1b00600d68a90e8cb8617105d09d
*: add initial torrent stuff
WOAH!
these checkboxes are a pain in my fucking ass
diff -r 32afe0e940bf -r ab191e28e69d CMakeLists.txt
--- a/CMakeLists.txt Mon Nov 06 13:48:11 2023 -0500
+++ b/CMakeLists.txt Tue Nov 07 08:03:42 2023 -0500
@@ -12,14 +12,7 @@
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
option(USE_QT6 "Force build with Qt 6" OFF)
option(USE_QT5 "Force build with Qt 5" OFF)
-# The reason I'm not specifying this an an option() is that
-# that will *save the value*, which causes the *.qm translation
-# files to not automatically be generated, screwing up the whole
-# "automation" part of it.
-#
-# Ugh.
-#
-# option(UPDATE_TRANSLATIONS "Update *.ts translation files" OFF)
+option(UPDATE_TRANSLATIONS "Update *.ts translation files" OFF)
add_subdirectory(dep/anitomy)
add_subdirectory(dep/animia)
@@ -45,6 +38,7 @@
${Qt${QT_VERSION_MAJOR}Widgets_LIBRARIES}
anitomy
animia
+ pugixml
)
# We need Cocoa for some OS X stuff
diff -r 32afe0e940bf -r ab191e28e69d include/core/config.h
--- a/include/core/config.h Mon Nov 06 13:48:11 2023 -0500
+++ b/include/core/config.h Tue Nov 07 08:03:42 2023 -0500
@@ -33,7 +33,13 @@
#define WIDEIFY_EX(x) L##x
#define WIDEIFY(x) WIDEIFY_EX(x)
+
+#if (defined(WIN32) || defined(MACOSX))
+#define CONFIG_DIR "Minori"
+#else
#define CONFIG_DIR "minori"
+#endif
+
#define CONFIG_WDIR WIDEIFY(CONFIG_DIR)
#define CONFIG_NAME "config.ini"
#define CONFIG_WNAME WIDEIFY(CONFIG_NAME)
diff -r 32afe0e940bf -r ab191e28e69d include/core/strings.h
--- a/include/core/strings.h Mon Nov 06 13:48:11 2023 -0500
+++ b/include/core/strings.h Tue Nov 07 08:03:42 2023 -0500
@@ -4,72 +4,12 @@
#include
#include
#include
+#include
class QString;
namespace Strings {
-templatestruct seq{using type=seq;};
-template
-struct gen_seq_x : gen_seq_x{};
-template
-struct gen_seq_x<0, Is...> : seq{};
-template
-using gen_seq=typename gen_seq_x::type;
-
-template
-using size=std::integral_constant;
-
-template
-constexpr size length( T const(&)[N] ) { return {}; }
-template
-constexpr size length( std::array const& ) { return {}; }
-
-template
-using length_t = decltype(length(std::declval()));
-
-constexpr size_t string_size() { return 0; }
-template
-constexpr size_t string_size( size_t i, Ts... ts ) {
- return (i?i-1:0) + string_size(ts...);
-}
-template
-using string_length=size< string_size( length_t{}... )>;
-
-template
-using combined_string = std::array{}+1>;
-
-template
-constexpr const combined_string
-concat_impl( Lhs const& lhs, Rhs const& rhs, seq, seq)
-{
- return {{ lhs[I1]..., rhs[I2]..., '\0' }};
-}
-
-template
-constexpr const combined_string
-concat(Lhs const& lhs, Rhs const& rhs)
-{
- return concat_impl(lhs, rhs, gen_seq{}>{}, gen_seq{}>{});
-}
-
-template
-constexpr const combined_string
-concat(T0 const&t0, T1 const&t1, Ts const&...ts)
-{
- return concat(t0, concat(t1, ts...));
-}
-
-template
-constexpr const combined_string
-concat(T const&t) {
- return concat(t, "");
-}
-constexpr const combined_string<>
-concat() {
- return concat("");
-}
-
/* Implode function: takes a vector of strings and turns it
into a string, separated by delimiters. */
std::string Implode(const std::vector& vector, const std::string& delimiter);
@@ -98,6 +38,11 @@
/* arithmetic :) */
int ToInt(const std::string& str, int def = 0);
+uint64_t HumanReadableSizeToBytes(const std::string& str);
+
+std::string RemoveLeadingChars(std::string s, const char c);
+std::string RemoveTrailingChars(std::string s, const char c);
+
bool BeginningMatchesSubstring(const std::string& str, const std::string& sub);
}; // namespace Strings
diff -r 32afe0e940bf -r ab191e28e69d include/core/torrent.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/core/torrent.h Tue Nov 07 08:03:42 2023 -0500
@@ -0,0 +1,56 @@
+#ifndef __core__torrent_h
+#define __core__torrent_h
+
+#include
+#include
+
+/* this will be moved into its own namespace if
+ it's deemed necessary */
+class Torrent {
+ public:
+ std::string GetTitle() const { return _title; };
+ std::string GetEpisode() const { return _episode; };
+ std::string GetGroup() const { return _group; };
+ size_t GetSize() const { return _size; };
+ std::string GetResolution() const { return _resolution; };
+ int GetSeeders() const { return _seeders; };
+ int GetLeechers() const { return _leechers; };
+ int GetDownloaders() const { return _downloaders; };
+ std::string GetDescription() const { return _description; };
+ std::string GetFilename() const { return _filename; };
+ std::string GetLink() const { return _link; };
+ std::string GetGuid() const { return _guid; };
+ QDateTime GetDate() const { return _date; };
+
+ void SetTitle(const std::string& title) { _title = title; };
+ void SetEpisode(const std::string& episode) { _episode = episode; };
+ void SetGroup(const std::string& group) { _group = group; };
+ void SetSize(const size_t size) { _size = size; };
+ void SetResolution(const std::string& resolution) { _resolution = resolution; };
+ void SetSeeders(const int seeders) { _seeders = seeders; };
+ void SetLeechers(const int leechers) { _leechers = leechers; };
+ void SetDownloaders(const int downloaders) { _downloaders = downloaders; };
+ void SetDescription(const std::string& description) { _description = description; };
+ void SetFilename(const std::string& filename) { _filename = filename; };
+ void SetLink(const std::string& link) { _link = link; };
+ void SetGuid(const std::string& guid) { _guid = guid; };
+ void SetDate(const QDateTime& date) { _date = date; };
+
+ private:
+ std::string _title;
+ std::string _episode;
+ std::string _group;
+ size_t _size = 0;
+ std::string _resolution; /* technically should be an int,
+ but std::string is more useful */
+ int _seeders = 0;
+ int _leechers = 0;
+ int _downloaders = 0;
+ std::string _description;
+ std::string _filename;
+ std::string _link;
+ std::string _guid;
+ QDateTime _date;
+};
+
+#endif // __core__torrent_h
\ No newline at end of file
diff -r 32afe0e940bf -r ab191e28e69d include/gui/pages/anime_list.h
--- a/include/gui/pages/anime_list.h Mon Nov 06 13:48:11 2023 -0500
+++ b/include/gui/pages/anime_list.h Tue Nov 07 08:03:42 2023 -0500
@@ -41,7 +41,7 @@
NB_COLUMNS
};
- AnimeListPageModel(QWidget* parent, Anime::ListStatus _status);
+ AnimeListPageModel(QObject* parent, Anime::ListStatus _status);
~AnimeListPageModel() override = default;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
@@ -63,7 +63,6 @@
public:
AnimeListPage(QWidget* parent);
void Refresh();
- void Reset();
protected:
void paintEvent(QPaintEvent*) override;
diff -r 32afe0e940bf -r ab191e28e69d include/gui/pages/torrents.h
--- a/include/gui/pages/torrents.h Mon Nov 06 13:48:11 2023 -0500
+++ b/include/gui/pages/torrents.h Tue Nov 07 08:03:42 2023 -0500
@@ -1,13 +1,77 @@
#ifndef __gui__pages__torrents_h
#define __gui__pages__torrents_h
-#include
+#include "core/torrent.h"
+#include
+#include
+#include
+
+class TorrentModelItem : public Torrent {
+ public:
+ bool GetChecked() const { return _checked; };
+ void SetChecked(bool checked) { _checked = checked; };
+
+ private:
+ bool _checked = false;
+};
+
+class TorrentsPageListSortFilter final : public QSortFilterProxyModel {
+ Q_OBJECT
+
+ public:
+ TorrentsPageListSortFilter(QObject* parent = nullptr);
+
+ protected:
+ bool lessThan(const QModelIndex& l, const QModelIndex& r) const override;
+};
+
+class TorrentsPageListModel final : public QAbstractListModel {
+ Q_OBJECT
-class TorrentsPage final : public QWidget {
+ public:
+ enum columns {
+ TL_TITLE,
+ TL_EPISODE,
+ TL_GROUP,
+ TL_SIZE,
+ TL_RESOLUTION,
+ TL_SEEDERS,
+ TL_LEECHERS,
+ TL_DOWNLOADERS,
+ TL_DESCRIPTION,
+ TL_FILENAME,
+ TL_RELEASEDATE,
+
+ NB_COLUMNS
+ };
+
+ TorrentsPageListModel(QObject* parent);
+ ~TorrentsPageListModel() override = default;
+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex& parent = QModelIndex()) const override;
+ bool setData(const QModelIndex& index, const QVariant& value, int role) override;
+ QVariant data(const QModelIndex& index, int role) const override;
+ QVariant headerData(const int section, const Qt::Orientation orientation, const int role) const override;
+ Qt::ItemFlags flags(const QModelIndex& index) const override;
+
+ QByteArray DownloadTorrentList();
+ void ParseTorrentList(const QByteArray& ba);
+ void RefreshTorrentList();
+
+ private:
+ std::vector list;
+};
+
+class TorrentsPage final : public QFrame {
Q_OBJECT
public:
TorrentsPage(QWidget* parent = nullptr);
+ void Refresh();
+
+ private:
+ TorrentsPageListModel* model = nullptr;
+ TorrentsPageListSortFilter* sort_model = nullptr;
};
#endif // __gui__pages__torrents_h
diff -r 32afe0e940bf -r ab191e28e69d include/gui/window.h
--- a/include/gui/window.h Mon Nov 06 13:48:11 2023 -0500
+++ b/include/gui/window.h Tue Nov 07 08:03:42 2023 -0500
@@ -2,11 +2,15 @@
#define __window_h
#include "core/config.h"
#include
+#include
-class QWidget;
-class QStackedWidget;
-class QCloseEvent;
-class SideBar;
+/* *could* be forward-declared, but this causes
+ any file that #includes this to have to #include
+ these as well due to unique_ptr */
+#include
+#include
+#include
+#include "gui/widgets/sidebar.h"
class MainWindow final : public QMainWindow {
Q_OBJECT
@@ -23,9 +27,9 @@
void closeEvent(QCloseEvent* event) override;
private:
- QWidget* main_widget = nullptr;
- QStackedWidget* stack = nullptr;
- SideBar* sidebar = nullptr;
+ std::unique_ptr main_widget = nullptr;
+ std::unique_ptr stack = nullptr;
+ std::unique_ptr sidebar = nullptr;
};
#endif // __window_h
diff -r 32afe0e940bf -r ab191e28e69d include/track/media.h
--- a/include/track/media.h Mon Nov 06 13:48:11 2023 -0500
+++ b/include/track/media.h Tue Nov 07 08:03:42 2023 -0500
@@ -7,6 +7,7 @@
namespace Media {
Filesystem::Path GetCurrentPlaying();
+std::unordered_map GetFileElements(std::string basename);
std::unordered_map GetFileElements(Filesystem::Path path);
} // namespace Media
diff -r 32afe0e940bf -r ab191e28e69d rc/icons.qrc
--- a/rc/icons.qrc Mon Nov 06 13:48:11 2023 -0500
+++ b/rc/icons.qrc Tue Nov 07 08:03:42 2023 -0500
@@ -1,13 +1,17 @@
favicon.png
+ icons/16x16/arrow-circle-315.png
icons/16x16/calendar.png
icons/16x16/chart.png
icons/16x16/clock-history-frame.png
+ icons/16x16/cross-button.png
icons/16x16/document-list.png
icons/16x16/feed.png
icons/16x16/film.png
+ icons/16x16/gear.png
icons/16x16/magnifier.png
+ icons/16x16/navigation-270-button.png
icons/24x24/application-export.png
icons/24x24/application-sidebar-list.png
icons/24x24/arrow-circle-double-135.png
diff -r 32afe0e940bf -r ab191e28e69d rc/icons/16x16/arrow-circle-315.png
Binary file rc/icons/16x16/arrow-circle-315.png has changed
diff -r 32afe0e940bf -r ab191e28e69d rc/icons/16x16/cross-button.png
Binary file rc/icons/16x16/cross-button.png has changed
diff -r 32afe0e940bf -r ab191e28e69d rc/icons/16x16/gear.png
Binary file rc/icons/16x16/gear.png has changed
diff -r 32afe0e940bf -r ab191e28e69d rc/icons/16x16/navigation-270-button.png
Binary file rc/icons/16x16/navigation-270-button.png has changed
diff -r 32afe0e940bf -r ab191e28e69d src/core/strings.cc
--- a/src/core/strings.cc Mon Nov 06 13:48:11 2023 -0500
+++ b/src/core/strings.cc Tue Nov 07 08:03:42 2023 -0500
@@ -41,7 +41,17 @@
}
std::string SanitizeLineEndings(const std::string& string) {
- return ReplaceAll(ReplaceAll(ReplaceAll(string, "\r\n", "\n"), "
", "\n"), "\n\n\n", "\n\n");
+ /* LOL */
+ return
+ ReplaceAll(
+ ReplaceAll(
+ ReplaceAll(
+ ReplaceAll(
+ ReplaceAll(string, "\r\n", "\n"),
+ "
", "\n"),
+ "
", "\n"),
+ "
", "\n"),
+ "\n\n\n", "\n\n");
}
/* removes dumb HTML tags because anilist is aids and
@@ -146,6 +156,39 @@
return tmp;
}
+uint64_t HumanReadableSizeToBytes(const std::string& str) {
+ const std::unordered_map bytes_map = {
+ {"KB", 1 << 10},
+ {"MB", 1 << 20},
+ {"GB", 1 << 30},
+ {"TB", 1 << 40},
+ {"PB", 1 << 50} /* surely we won't need more than this */
+ };
+
+ for (const auto& suffix : bytes_map) {
+ if (str.find(suffix.first) != std::string::npos) {
+ try {
+ uint64_t size = std::stod(str) * suffix.second;
+ return size;
+ } catch (std::invalid_argument const& ex) {
+ continue;
+ }
+ }
+ }
+
+ return ToInt(str, 0);
+}
+
+std::string RemoveLeadingChars(std::string s, const char c) {
+ s.erase(0, std::min(s.find_first_not_of(c), s.size() - 1));
+ return s;
+}
+
+std::string RemoveTrailingChars(std::string s, const char c) {
+ s.erase(s.find_last_not_of(c) + 1, std::string::npos);
+ return s;
+}
+
bool BeginningMatchesSubstring(const std::string& str, const std::string& sub) {
for (unsigned long long i = 0; i < str.length() && i < sub.length(); i++)
if (str[i] != sub[i])
diff -r 32afe0e940bf -r ab191e28e69d src/gui/pages/anime_list.cc
--- a/src/gui/pages/anime_list.cc Mon Nov 06 13:48:11 2023 -0500
+++ b/src/gui/pages/anime_list.cc Tue Nov 07 08:03:42 2023 -0500
@@ -47,7 +47,9 @@
}
}
-AnimeListPageModel::AnimeListPageModel(QWidget* parent, Anime::ListStatus _status) : QAbstractListModel(parent) {
+/* -------------------------------------------------- */
+
+AnimeListPageModel::AnimeListPageModel(QObject* parent, Anime::ListStatus _status) : QAbstractListModel(parent) {
status = _status;
return;
}
@@ -180,6 +182,8 @@
endResetModel();
}
+/* ----------------------------------------------------------------- */
+
int AnimeListPage::VisibleColumnsCount() const {
int count = 0;
@@ -316,24 +320,24 @@
dialog->activateWindow();
}
-void AnimeListPage::paintEvent(QPaintEvent*) {
- QStylePainter p(this);
-
- QStyleOptionTabWidgetFrame opt;
- InitStyle(&opt);
- opt.rect = panelRect;
- p.drawPrimitive(QStyle::PE_FrameTabWidget, opt);
+void AnimeListPage::RefreshList() {
+ for (unsigned int i = 0; i < sort_models.size(); i++)
+ reinterpret_cast(sort_models[i]->sourceModel())->RefreshList();
}
-void AnimeListPage::resizeEvent(QResizeEvent* e) {
- QWidget::resizeEvent(e);
- SetupLayout();
+void AnimeListPage::RefreshTabs() {
+ for (unsigned int i = 0; i < sort_models.size(); i++)
+ tab_bar->setTabText(i, Strings::ToQString(Translate::ToString(Anime::ListStatuses[i])) + " (" +
+ QString::number(Anime::db.GetListsAnimeAmount(Anime::ListStatuses[i])) + ")");
}
-void AnimeListPage::showEvent(QShowEvent*) {
- SetupLayout();
+void AnimeListPage::Refresh() {
+ RefreshList();
+ RefreshTabs();
}
+/* -------- QTabWidget replication begin --------- */
+
void AnimeListPage::InitBasicStyle(QStyleOptionTabWidgetFrame* option) const {
if (!option)
return;
@@ -378,6 +382,26 @@
tree_view->parentWidget()->setGeometry(contentsRect);
}
+void AnimeListPage::paintEvent(QPaintEvent*) {
+ QStylePainter p(this);
+
+ QStyleOptionTabWidgetFrame opt;
+ InitStyle(&opt);
+ opt.rect = panelRect;
+ p.drawPrimitive(QStyle::PE_FrameTabWidget, opt);
+}
+
+void AnimeListPage::resizeEvent(QResizeEvent* e) {
+ QWidget::resizeEvent(e);
+ SetupLayout();
+}
+
+void AnimeListPage::showEvent(QShowEvent*) {
+ SetupLayout();
+}
+
+/* --------- QTabWidget replication end ---------- */
+
AnimeListPage::AnimeListPage(QWidget* parent) : QWidget(parent) {
/* Tab bar */
tab_bar = new QTabBar(this);
@@ -438,30 +462,4 @@
Refresh();
}
-void AnimeListPage::RefreshList() {
- for (unsigned int i = 0; i < sort_models.size(); i++)
- reinterpret_cast(sort_models[i]->sourceModel())->RefreshList();
-}
-
-void AnimeListPage::RefreshTabs() {
- for (unsigned int i = 0; i < sort_models.size(); i++)
- tab_bar->setTabText(i, Strings::ToQString(Translate::ToString(Anime::ListStatuses[i])) + " (" +
- QString::number(Anime::db.GetListsAnimeAmount(Anime::ListStatuses[i])) + ")");
-}
-
-void AnimeListPage::Refresh() {
- RefreshList();
- RefreshTabs();
-}
-
-/* This function, really, really should not be called.
- Ever. Why would you ever need to clear the anime list?
- Also, this sucks. */
-void AnimeListPage::Reset() {
- while (tab_bar->count())
- tab_bar->removeTab(0);
- for (unsigned int i = 0; i < sort_models.size(); i++)
- delete sort_models[i];
-}
-
#include "gui/pages/moc_anime_list.cpp"
diff -r 32afe0e940bf -r ab191e28e69d src/gui/pages/torrents.cc
--- a/src/gui/pages/torrents.cc Mon Nov 06 13:48:11 2023 -0500
+++ b/src/gui/pages/torrents.cc Tue Nov 07 08:03:42 2023 -0500
@@ -1,6 +1,331 @@
#include "gui/pages/torrents.h"
+#include "core/strings.h"
+#include "core/http.h"
+#include "core/session.h"
+#include "gui/widgets/text.h"
+#include "track/media.h"
+#include "pugixml.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+TorrentsPageListSortFilter::TorrentsPageListSortFilter(QObject* parent) : QSortFilterProxyModel(parent) {
+}
+
+bool TorrentsPageListSortFilter::lessThan(const QModelIndex& l, const QModelIndex& r) const {
+ QVariant left = sourceModel()->data(l, sortRole());
+ QVariant right = sourceModel()->data(r, sortRole());
+
+ switch (left.userType()) {
+ case QMetaType::Int:
+ case QMetaType::UInt:
+ case QMetaType::LongLong:
+ case QMetaType::ULongLong: return left.toInt() < right.toInt();
+ case QMetaType::QDate: return left.toDate() < right.toDate();
+ case QMetaType::QString:
+ default: return QString::compare(left.toString(), right.toString(), Qt::CaseInsensitive) < 0;
+ }
+}
+
+/* -------------------------------------------- */
+
+TorrentsPageListModel::TorrentsPageListModel(QObject* parent) : QAbstractListModel(parent) {
+}
+
+QByteArray TorrentsPageListModel::DownloadTorrentList() {
+ return HTTP::Get("https://www.tokyotosho.info/rss.php?filter=1,11&zwnj=0");
+}
+
+void TorrentsPageListModel::ParseTorrentList(const QByteArray& ba) {
+ std::istringstream stdstream(Strings::ToUtf8String(ba));
+
+ pugi::xml_document doc;
+ if (!doc.load(stdstream))
+ return; // peace out
+
+ /* my extra special dumb hack. */
+ if (!rowCount(index(0))) {
+ beginInsertRows(QModelIndex(), 0, 0);
+ endInsertRows();
+ }
+
+ beginResetModel();
+
+ list.clear();
+ /* this is just an rss parser; it should be in a separate class... */
+ for (pugi::xml_node item : doc.child("rss").child("channel").children("item")) {
+ TorrentModelItem torrent;
+ torrent.SetFilename(item.child_value("title")); /* "title" == filename */
+ {
+ /* Use Anitomy to parse the file's elements (we should *really* not be doing this this way!) */
+ std::unordered_map elements = Track::Media::GetFileElements(torrent.GetFilename());
+ torrent.SetTitle(elements["title"]);
+ torrent.SetEpisode(Strings::RemoveLeadingChars(elements["episode"], '0'));
+ torrent.SetGroup(elements["group"]);
+ torrent.SetResolution(elements["resolution"]);
+ }
+ torrent.SetDescription(Strings::TextifySynopsis(item.child_value("description")));
+ {
+ /* Parse size from description */
+ std::istringstream descstream(torrent.GetDescription());
+
+ for (std::string line; std::getline(descstream, line);) {
+ const std::string match = "Size: ";
+ size_t pos = line.find(match);
+
+ if (!pos) {
+ const std::string size = line.substr(pos + match.length());
+ torrent.SetSize(Strings::HumanReadableSizeToBytes(size));
+ }
+ }
+ }
+ torrent.SetLink(item.child_value("link"));
+ torrent.SetGuid(item.child_value("guid"));
+ {
+ const QString date_str = Strings::ToQString(item.child_value("pubDate"));
+ torrent.SetDate(QDateTime::fromString(date_str, "ddd, dd MMM yyyy HH:mm:ss t"));
+ }
+ list.push_back(torrent);
+ }
+
+ endResetModel();
+}
+
+void TorrentsPageListModel::RefreshTorrentList() {
+ ParseTorrentList(DownloadTorrentList());
+}
+
+int TorrentsPageListModel::rowCount(const QModelIndex& parent) const {
+ return list.size();
+ (void)(parent);
+}
+
+int TorrentsPageListModel::columnCount(const QModelIndex& parent) const {
+ return NB_COLUMNS;
+ (void)(parent);
+}
+
+QVariant TorrentsPageListModel::headerData(const int section, const Qt::Orientation orientation, const int role) const {
+ switch (role) {
+ case Qt::DisplayRole: {
+ switch (section) {
+ case TL_TITLE: return tr("Anime title");
+ case TL_EPISODE: return tr("Episode");
+ case TL_GROUP: return tr("Group");
+ case TL_SIZE: return tr("Size");
+ case TL_RESOLUTION: return tr("Resolution"); /* this is named "Video" in Taiga */
+ case TL_SEEDERS: return tr("Seeding"); /* named "S" in Taiga */
+ case TL_LEECHERS: return tr("Leeching"); /* named "L" in Taiga */
+ case TL_DOWNLOADERS: return tr("Downloading"); /* named "D" in Taiga */
+ case TL_DESCRIPTION: return tr("Description");
+ case TL_FILENAME: return tr("Filename");
+ case TL_RELEASEDATE: return tr("Release date");
+ default: return {};
+ }
+ break;
+ }
+ case Qt::TextAlignmentRole: {
+ switch (section) {
+ case TL_FILENAME:
+ case TL_GROUP:
+ case TL_DESCRIPTION:
+ case TL_RESOLUTION:
+ case TL_TITLE: return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
+ case TL_SEEDERS:
+ case TL_LEECHERS:
+ case TL_DOWNLOADERS:
+ case TL_SIZE:
+ case TL_EPISODE:
+ case TL_RELEASEDATE: return QVariant(Qt::AlignRight | Qt::AlignVCenter);
+ default: return {};
+ }
+ break;
+ }
+ }
+ return QAbstractListModel::headerData(section, orientation, role);
+}
+
+bool TorrentsPageListModel::setData(const QModelIndex& index, const QVariant& value, int role) {
+ TorrentModelItem& item = list.at(index.row());
-TorrentsPage::TorrentsPage(QWidget* parent) : QWidget(parent) {
+ if (index.column() == 0) {
+ switch (role) {
+ case Qt::EditRole:
+ return false;
+ case Qt::CheckStateRole:
+ item.SetChecked(value.toBool());
+ emit dataChanged(index, index);
+ return true;
+ }
+ }
+
+ return QAbstractItemModel::setData(index, value, role);
+}
+
+QVariant TorrentsPageListModel::data(const QModelIndex& index, int role) const {
+ if (!index.isValid())
+ return QVariant();
+ switch (role) {
+ case Qt::DisplayRole:
+ switch (index.column()) {
+ case TL_TITLE: return Strings::ToQString(list.at(index.row()).GetTitle());
+ case TL_EPISODE: return Strings::ToQString(list.at(index.row()).GetEpisode());
+ case TL_GROUP: return Strings::ToQString(list.at(index.row()).GetGroup());
+ case TL_SIZE: return session.config.locale.GetLocale().formattedDataSize(list.at(index.row()).GetSize());
+ case TL_RESOLUTION: return Strings::ToQString(list.at(index.row()).GetResolution());
+ case TL_SEEDERS: return list.at(index.row()).GetSeeders();
+ case TL_LEECHERS: return list.at(index.row()).GetLeechers();
+ case TL_DOWNLOADERS: return list.at(index.row()).GetDownloaders();
+ case TL_DESCRIPTION: return Strings::ToQString(list.at(index.row()).GetDescription());
+ case TL_FILENAME: return Strings::ToQString(list.at(index.row()).GetFilename());
+ case TL_RELEASEDATE: return list.at(index.row()).GetDate();
+ default: return "";
+ }
+ break;
+ case Qt::UserRole:
+ switch (index.column()) {
+ case TL_EPISODE: return Strings::ToInt(list.at(index.row()).GetEpisode(), -1);
+ case TL_SIZE: return list.at(index.row()).GetSize();
+ default: return data(index, Qt::DisplayRole);
+ }
+ break;
+ case Qt::CheckStateRole:
+ switch (index.column()) {
+ case 0: return list.at(index.row()).GetChecked() ? Qt::Checked : Qt::Unchecked;
+ default: return {};
+ }
+ case Qt::SizeHintRole: {
+ switch (index.column()) {
+ default: {
+ const QString d = data(index, Qt::DisplayRole).toString();
+ const QFontMetrics metric = QFontMetrics(QFont());
+
+ return QSize(std::max(metric.horizontalAdvance(d), 100), metric.height());
+ }
+ }
+ break;
+ }
+ case Qt::TextAlignmentRole:
+ switch (index.column()) {
+ case TL_FILENAME:
+ case TL_GROUP:
+ case TL_DESCRIPTION:
+ case TL_RESOLUTION:
+ case TL_TITLE: return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
+ case TL_SEEDERS:
+ case TL_LEECHERS:
+ case TL_DOWNLOADERS:
+ case TL_SIZE:
+ case TL_EPISODE:
+ case TL_RELEASEDATE: return QVariant(Qt::AlignRight | Qt::AlignVCenter);
+ default: return {};
+ }
+ break;
+ }
+ return QVariant();
+}
+
+Qt::ItemFlags TorrentsPageListModel::flags(const QModelIndex& index) const {
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+
+ const TorrentModelItem& item = list.at(index.row());
+
+ if (item.GetChecked() || index.column() == 0)
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
+ else
+ return Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
+}
+
+TorrentsPage::TorrentsPage(QWidget* parent) : QFrame(parent) {
+ setFrameShape(QFrame::Box);
+ setFrameShadow(QFrame::Sunken);
+
+ QVBoxLayout* layout = new QVBoxLayout(this);
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->setSpacing(0);
+
+ {
+ /* Toolbar */
+ QToolBar* toolbar = new QToolBar(this);
+ toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+ toolbar->setIconSize(QSize(16, 16));
+ toolbar->setMovable(false);
+
+ {
+ /* this needs to be stored somewhere to replicate Taiga's
+ "timer" feature */
+ QAction* action = toolbar->addAction(QIcon(":/icons/16x16/arrow-circle-315.png"), tr("&Check new torrents"), [this]{
+ QThreadPool::globalInstance()->start([this] {
+ Refresh();
+ });
+ });
+ }
+
+ toolbar->addSeparator();
+
+ {
+ QAction* action = toolbar->addAction(QIcon(":/icons/16x16/navigation-270-button.png"), tr("Download &marked torrents"));
+ }
+
+ {
+ QAction* action = toolbar->addAction(QIcon(":/icons/16x16/cross-button.png"), tr("&Discard all"));
+ }
+
+ toolbar->addSeparator();
+
+ {
+ QAction* action = toolbar->addAction(QIcon(":/icons/16x16/gear.png"), tr("&Settings"));
+ }
+
+ layout->addWidget(toolbar);
+ }
+
+ {
+ QFrame* line = new QFrame(this);
+ line->setFrameShape(QFrame::HLine);
+ line->setFrameShadow(QFrame::Sunken);
+ line->setLineWidth(1);
+ layout->addWidget(line);
+ }
+
+ {
+ QTreeView* treeview = new QTreeView(this);
+ treeview->setUniformRowHeights(true);
+ treeview->setAllColumnsShowFocus(false);
+ treeview->setAlternatingRowColors(true);
+ treeview->setSortingEnabled(true);
+ treeview->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ treeview->setItemsExpandable(false);
+ treeview->setRootIsDecorated(false);
+ treeview->setContextMenuPolicy(Qt::CustomContextMenu);
+ treeview->setFrameShape(QFrame::NoFrame);
+
+ {
+ sort_model = new TorrentsPageListSortFilter(treeview);
+ model = new TorrentsPageListModel(treeview);
+ sort_model->setSourceModel(model);
+ sort_model->setSortRole(Qt::UserRole);
+ sort_model->setSortCaseSensitivity(Qt::CaseInsensitive);
+ treeview->setModel(sort_model);
+ }
+
+ layout->addWidget(treeview);
+ }
+}
+
+void TorrentsPage::Refresh() {
+ if (!model)
+ return;
+ model->RefreshTorrentList();
}
#include "gui/pages/moc_torrents.cpp"
diff -r 32afe0e940bf -r ab191e28e69d src/gui/window.cc
--- a/src/gui/window.cc Mon Nov 06 13:48:11 2023 -0500
+++ b/src/gui/window.cc Tue Nov 07 08:03:42 2023 -0500
@@ -53,12 +53,12 @@
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) {
setWindowIcon(QIcon(":/favicon.png"));
- main_widget = new QWidget(this);
- /*QHBoxLayout* layout = */new QHBoxLayout(main_widget);
+ main_widget.reset(new QWidget(this));
+ /*QHBoxLayout* layout = */new QHBoxLayout(main_widget.get());
AddMainWidgets();
- setCentralWidget(main_widget);
+ setCentralWidget(main_widget.get());
CreateBars();
@@ -81,18 +81,17 @@
void MainWindow::AddMainWidgets() {
int page = static_cast(Pages::ANIME_LIST);
- if (sidebar) {
- main_widget->layout()->removeWidget(sidebar);
- delete sidebar;
+ if (sidebar.get()) {
+ main_widget->layout()->removeWidget(sidebar.get());
+ sidebar.reset();
}
- if (stack) {
+ if (stack.get()) {
page = stack->currentIndex();
- main_widget->layout()->removeWidget(stack);
- delete stack;
+ main_widget->layout()->removeWidget(stack.get());
}
- sidebar = new SideBar(main_widget);
+ sidebar.reset(new SideBar(main_widget.get()));
sidebar->setFixedWidth(128);
sidebar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
@@ -106,182 +105,284 @@
sidebar->AddItem(tr("Seasons"), SideBar::CreateIcon(":/icons/16x16/calendar.png"));
sidebar->AddItem(tr("Torrents"), SideBar::CreateIcon(":/icons/16x16/feed.png"));
- stack = new QStackedWidget(main_widget);
- 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));
+ stack.reset(new QStackedWidget(main_widget.get()));
+ stack->addWidget(new NowPlayingPage(main_widget.get()));
+ stack->addWidget(new AnimeListPage(main_widget.get()));
+ stack->addWidget(new HistoryPage(main_widget.get()));
+ stack->addWidget(new StatisticsPage(main_widget.get()));
+ stack->addWidget(new SearchPage(main_widget.get()));
+ stack->addWidget(new SeasonsPage(main_widget.get()));
+ stack->addWidget(new TorrentsPage(main_widget.get()));
- connect(sidebar, &SideBar::CurrentItemChanged, stack, &QStackedWidget::setCurrentIndex);
+ connect(sidebar.get(), &SideBar::CurrentItemChanged, stack.get(), &QStackedWidget::setCurrentIndex);
sidebar->SetCurrentItem(page);
- main_widget->layout()->addWidget(sidebar);
- main_widget->layout()->addWidget(stack);
+ main_widget->layout()->addWidget(sidebar.get());
+ main_widget->layout()->addWidget(stack.get());
}
void MainWindow::CreateBars() {
- /* Menu Bar */
- QAction* action;
+ /* Menu Bar
+ The notation of these might seem ugly at first, but it's actually very nice
+ (just trust me). It makes it much easier to edit the lists and makes it clear
+ if you're in submenu or not. */
QMenuBar* menubar = new QMenuBar(this);
- QMenu* menu = menubar->addMenu(tr("&File"));
- QMenu* submenu = menu->addMenu(tr("&Library folders"));
- action = submenu->addAction(tr("&Add new folder..."));
+ {
+ /* File */
+ QMenu* menu = menubar->addMenu(tr("&File"));
- action = menu->addAction(tr("&Scan available episodes"));
-
- menu->addSeparator();
+ {
+ QMenu* submenu = menu->addMenu(tr("&Library folders"));
+ {
+ QAction* action = submenu->addAction(tr("&Add new folder..."));
+ }
+ }
- action = menu->addAction(tr("Play &next episode"));
- action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_N));
- action = menu->addAction(tr("Play &random episode"));
- action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_R));
+ {
+ QAction* action = menu->addAction(tr("&Scan available episodes"));
+ }
- menu->addSeparator();
+ menu->addSeparator();
- action = menu->addAction(tr("E&xit"), qApp, &QApplication::quit);
+// {
+// QAction* action = menu->addAction(tr("Play &next episode"));
+// action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_N));
+// }
+//
+// {
+// QAction* action = menu->addAction(tr("Play &random episode"));
+// action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_R));
+// }
- menu = menubar->addMenu(tr("&Services"));
- action = menu->addAction(tr("Synchronize &list"), [this] { AsyncSynchronize(stack); });
- action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S));
-
- menu->addSeparator();
+ menu->addSeparator();
- submenu = menu->addMenu(tr("&AniList"));
- action = submenu->addAction(tr("Go to my &profile"));
- action = submenu->addAction(tr("Go to my &stats"));
+ {
+ QAction* action = menu->addAction(tr("E&xit"), qApp, &QApplication::quit);
+ }
+ }
- submenu = menu->addMenu(tr("&Kitsu"));
- action = submenu->addAction(tr("Go to my &feed"));
- action = submenu->addAction(tr("Go to my &library"));
- action = submenu->addAction(tr("Go to my &profile"));
-
- submenu = menu->addMenu(tr("&MyAnimeList"));
- action = submenu->addAction(tr("Go to my p&anel"));
- action = submenu->addAction(tr("Go to my &profile"));
- action = submenu->addAction(tr("Go to my &history"));
+ {
+ /* Services */
+ QMenu* menu = menubar->addMenu(tr("&Services"));
+ {
+ {
+ QAction* action = menu->addAction(tr("Synchronize &list"), [this] { AsyncSynchronize(stack.get()); });
+ action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S));
+ }
- menu = menubar->addMenu(tr("&Tools"));
- submenu = menu->addMenu(tr("&Export anime list"));
- action = submenu->addAction(tr("Export as &Markdown..."));
- action = submenu->addAction(tr("Export as MyAnimeList &XML..."));
-
- menu->addSeparator();
-
- action = menu->addAction(tr("Enable anime &recognition"));
- action->setCheckable(true);
- action = menu->addAction(tr("Enable auto &sharing"));
- action->setCheckable(true);
- action = menu->addAction(tr("Enable &auto synchronization"));
- action->setCheckable(true);
-
- menu->addSeparator();
+// menu->addSeparator();
+//
+// {
+// /* AniList */
+// QMenu* submenu = menu->addMenu(tr("&AniList"));
+// QAction* action = submenu->addAction(tr("Go to my &profile"));
+// action = submenu->addAction(tr("Go to my &stats"));
+// }
+//
+// {
+// /* Kitsu */
+// QMenu* submenu = menu->addMenu(tr("&Kitsu"));
+// QAction* action = submenu->addAction(tr("Go to my &feed"));
+// action = submenu->addAction(tr("Go to my &library"));
+// action = submenu->addAction(tr("Go to my &profile"));
+// }
+// {
+// QMenu* submenu = menu->addMenu(tr("&MyAnimeList"));
+// QAction* action = submenu->addAction(tr("Go to my p&anel"));
+// action = submenu->addAction(tr("Go to my &profile"));
+// action = submenu->addAction(tr("Go to my &history"));
+// }
+ }
+ }
- action = menu->addAction(tr("&Settings"), [this] {
- SettingsDialog dialog(this);
- dialog.exec();
- });
- action->setMenuRole(QAction::PreferencesRole);
-
- menu = menubar->addMenu(tr("&View"));
-
- std::map page_to_index_map = {};
-
- QActionGroup* pages_group = new QActionGroup(this);
- pages_group->setExclusive(true);
+ {
+ /* Tools */
+ QMenu* menu = menubar->addMenu(tr("&Tools"));
+// {
+// /* Export anime list */
+// QMenu* submenu = menu->addMenu(tr("&Export anime list"));
+//
+// {
+// /* Markdown export */
+// QAction* action = submenu->addAction(tr("Export as &Markdown..."));
+// }
+//
+// {
+// /* XML export */
+// QAction* action = submenu->addAction(tr("Export as MyAnimeList &XML..."));
+// }
+// }
+// menu->addSeparator();
+//
+// {
+// QAction* action = menu->addAction(tr("Enable anime &recognition"));
+// action->setCheckable(true);
+// }
+//
+// {
+// QAction* action = menu->addAction(tr("Enable auto &sharing"));
+// action->setCheckable(true);
+// }
+//
+// {
+// QAction* action = menu->addAction(tr("Enable &auto synchronization"));
+// action->setCheckable(true);
+// }
+//
+// menu->addSeparator();
- action = pages_group->addAction(menu->addAction(tr("&Now Playing")));
- action->setCheckable(true);
- page_to_index_map[action] = 0;
+ {
+ QAction* action = menu->addAction(tr("&Settings"), [this] {
+ SettingsDialog dialog(this);
+ dialog.exec();
+ });
+ action->setMenuRole(QAction::PreferencesRole);
+ }
+ }
+
+ {
+ /* View */
+ QMenu* menu = menubar->addMenu(tr("&View"));
- action = pages_group->addAction(menu->addAction(tr("&Anime List")));
- page_to_index_map[action] = 1;
- action->setCheckable(true);
- action->setChecked(true);
+ {
+ /* Pages... */
+ std::map page_to_index_map = {};
- action = pages_group->addAction(menu->addAction(tr("&History")));
- action->setCheckable(true);
- page_to_index_map[action] = 2;
+ QActionGroup* pages_group = new QActionGroup(this);
+ pages_group->setExclusive(true);
- action = pages_group->addAction(menu->addAction(tr("&Statistics")));
- action->setCheckable(true);
- page_to_index_map[action] = 3;
+ {
+ QAction* action = pages_group->addAction(menu->addAction(tr("&Now Playing")));
+ action->setCheckable(true);
+ page_to_index_map[action] = 0;
+ }
- action = pages_group->addAction(menu->addAction(tr("S&earch")));
- action->setCheckable(true);
- page_to_index_map[action] = 4;
+ {
+ QAction* action = pages_group->addAction(menu->addAction(tr("&Anime List")));
+ action->setCheckable(true);
+ action->setChecked(true);
+ page_to_index_map[action] = 1;
+ }
+
+ {
+ QAction* action = pages_group->addAction(menu->addAction(tr("&History")));
+ action->setCheckable(true);
+ page_to_index_map[action] = 2;
+ }
- action = pages_group->addAction(menu->addAction(tr("Se&asons")));
- action->setCheckable(true);
- page_to_index_map[action] = 5;
+ {
+ QAction* action = pages_group->addAction(menu->addAction(tr("&Statistics")));
+ action->setCheckable(true);
+ page_to_index_map[action] = 3;
+ }
- action = pages_group->addAction(menu->addAction(tr("&Torrents")));
- action->setCheckable(true);
- page_to_index_map[action] = 6;
+ {
+ QAction* action = pages_group->addAction(menu->addAction(tr("S&earch")));
+ action->setCheckable(true);
+ page_to_index_map[action] = 4;
+ }
- connect(sidebar, &SideBar::CurrentItemChanged, this,
- [pages_group](int index) { pages_group->actions()[index]->setChecked(true); });
+ {
+ QAction* action = pages_group->addAction(menu->addAction(tr("Se&asons")));
+ action->setCheckable(true);
+ page_to_index_map[action] = 5;
+ }
- connect(pages_group, &QActionGroup::triggered, this,
- [this, page_to_index_map](QAction* action) { sidebar->SetCurrentItem(page_to_index_map.at(action)); });
+ {
+ QAction* action = pages_group->addAction(menu->addAction(tr("&Torrents")));
+ action->setCheckable(true);
+ page_to_index_map[action] = 6;
+ }
- menu->addSeparator();
- menu->addAction(tr("Show sidebar"));
+ /* pain in my ass */
+ connect(sidebar.get(), &SideBar::CurrentItemChanged, this,
+ [pages_group](int index) { pages_group->actions()[index]->setChecked(true); });
+
+ connect(pages_group, &QActionGroup::triggered, this,
+ [this, page_to_index_map](QAction* action) { sidebar->SetCurrentItem(page_to_index_map.at(action)); });
+ }
+
+ menu->addSeparator();
- menu = menubar->addMenu(tr("&Help"));
- action = menu->addAction(tr("&About Minori"), this, [this] {
- AboutWindow dialog(this);
- dialog.exec();
- });
- action = menu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
- action->setMenuRole(QAction::AboutQtRole);
+// {
+// QAction* action = menu->addAction(tr("Show sidebar"));
+// }
+ }
+
+ {
+ /* Help */
+ QMenu* menu = menubar->addMenu(tr("&Help"));
+ {
+ /* About Minori */
+ menu->addAction(tr("&About Minori"), this, [this] {
+ AboutWindow dialog(this);
+ dialog.exec();
+ });
+ }
+
+ {
+ /* About Qt */
+ QAction* action = menu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
+ action->setMenuRole(QAction::AboutQtRole);
+ }
+ }
/* QMainWindow will delete the old one for us,
according to the docs */
setMenuBar(menubar);
/* Toolbar */
+
/* remove old toolbar(s) */
- QList toolbars = findChildren();
- for (auto& t : toolbars) {
+ for (QToolBar*& t : findChildren(Qt::FindDirectChildrenOnly)) {
removeToolBar(t);
delete t;
}
- QToolBar* toolbar = new QToolBar(this);
- toolbar->addAction(QIcon(":/icons/24x24/arrow-circle-double-135.png"), tr("&Synchronize"),
- [this] { AsyncSynchronize(stack); });
- toolbar->addSeparator();
+ {
+ /* Toolbar */
+ QToolBar* toolbar = new QToolBar(this);
+ toolbar->addAction(QIcon(":/icons/24x24/arrow-circle-double-135.png"), tr("&Synchronize"),
+ [this] { AsyncSynchronize(stack.get()); });
- QToolButton* button = new QToolButton(toolbar);
+ toolbar->addSeparator();
- menu = new QMenu(button);
- action = menu->addAction(tr("Add new folder..."));
+ {
+ QToolButton* button = new QToolButton(toolbar);
+ {
+ QMenu* menu = new QMenu(button);
+ QAction* action = menu->addAction(tr("..."));
- button->setMenu(menu);
- button->setIcon(QIcon(":/icons/24x24/folder-open.png"));
- button->setPopupMode(QToolButton::InstantPopup);
- toolbar->addWidget(button);
+ button->setMenu(menu);
+ }
+ button->setIcon(QIcon(":/icons/24x24/folder-open.png"));
+ button->setPopupMode(QToolButton::InstantPopup);
+ toolbar->addWidget(button);
+ }
- button = new QToolButton(toolbar);
+ {
+ QToolButton* button = new QToolButton(toolbar);
- menu = new QMenu(button);
- action = menu->addAction(tr("Placeholder"));
+ {
+ QMenu* menu = new QMenu(button);
+ QAction* action = menu->addAction(tr("..."));
+
+ button->setMenu(menu);
+ }
- button->setMenu(menu);
- button->setIcon(QIcon(":/icons/24x24/application-export.png"));
- button->setPopupMode(QToolButton::InstantPopup);
- toolbar->addWidget(button);
+ button->setIcon(QIcon(":/icons/24x24/application-export.png"));
+ button->setPopupMode(QToolButton::InstantPopup);
+ toolbar->addWidget(button);
+ }
- toolbar->addSeparator();
- toolbar->addAction(QIcon(":/icons/24x24/gear.png"), tr("S&ettings"), [this] {
- SettingsDialog dialog(this);
- dialog.exec();
- });
- addToolBar(toolbar);
+ toolbar->addSeparator();
+ toolbar->addAction(QIcon(":/icons/24x24/gear.png"), tr("S&ettings"), [this] {
+ SettingsDialog dialog(this);
+ dialog.exec();
+ });
+ addToolBar(toolbar);
+ }
}
void MainWindow::SetActivePage(QWidget* page) {
diff -r 32afe0e940bf -r ab191e28e69d src/track/media.cc
--- a/src/track/media.cc Mon Nov 06 13:48:11 2023 -0500
+++ b/src/track/media.cc Tue Nov 07 08:03:42 2023 -0500
@@ -40,10 +40,18 @@
ret["language"] = Strings::ToUtf8String(elements.get(anitomy::kElementLanguage));
ret["group"] = Strings::ToUtf8String(elements.get(anitomy::kElementReleaseGroup));
ret["episode"] = Strings::ToUtf8String(elements.get(anitomy::kElementEpisodeNumber));
+ ret["resolution"] = Strings::ToUtf8String(elements.get(anitomy::kElementVideoResolution));
return ret;
}
+std::unordered_map GetFileElements(std::string basename) {
+ anitomy::Anitomy anitomy;
+ anitomy.Parse(Strings::ToWstring(basename));
+
+ return GetMapFromElements(anitomy.elements());
+}
+
std::unordered_map GetFileElements(Filesystem::Path path) {
anitomy::Anitomy anitomy;
anitomy.Parse(Strings::ToWstring(path.Basename()));