Mercurial > minori
comparison src/gui/pages/torrents.cc @ 370:ea3a74ed2ef9
*: hm, last commit wasn't quite finished?
author | Paper <paper@tflc.us> |
---|---|
date | Fri, 25 Jul 2025 10:22:04 -0400 |
parents | 8d45d892be88 |
children |
comparison
equal
deleted
inserted
replaced
369:47c9f8502269 | 370:ea3a74ed2ef9 |
---|---|
30 * It differs from Taiga in that it uses tabs instead of | 30 * It differs from Taiga in that it uses tabs instead of |
31 * those "groups", but those are custom painted and a pain in the ass to | 31 * those "groups", but those are custom painted and a pain in the ass to |
32 * maintain over multiple platforms. | 32 * maintain over multiple platforms. |
33 */ | 33 */ |
34 | 34 |
35 TorrentsPageListSortFilter::TorrentsPageListSortFilter(QObject* parent) : QSortFilterProxyModel(parent) { | 35 TorrentsPageListSortFilter::TorrentsPageListSortFilter(QObject *parent) : QSortFilterProxyModel(parent) |
36 } | 36 { |
37 | 37 } |
38 bool TorrentsPageListSortFilter::lessThan(const QModelIndex& l, const QModelIndex& r) const { | 38 |
39 bool TorrentsPageListSortFilter::lessThan(const QModelIndex &l, const QModelIndex &r) const | |
40 { | |
39 QVariant left = sourceModel()->data(l, sortRole()); | 41 QVariant left = sourceModel()->data(l, sortRole()); |
40 QVariant right = sourceModel()->data(r, sortRole()); | 42 QVariant right = sourceModel()->data(r, sortRole()); |
41 | 43 |
42 switch (left.userType()) { | 44 switch (left.userType()) { |
43 case QMetaType::Int: | 45 case QMetaType::Int: |
50 } | 52 } |
51 } | 53 } |
52 | 54 |
53 /* -------------------------------------------- */ | 55 /* -------------------------------------------- */ |
54 | 56 |
55 TorrentsPageListModel::TorrentsPageListModel(QObject* parent) : QAbstractListModel(parent) { | 57 TorrentsPageListModel::TorrentsPageListModel(QObject *parent) : QAbstractListModel(parent) |
56 } | 58 { |
57 | 59 } |
58 void TorrentsPageListModel::DownloadTorrents(QItemSelection selection) { | 60 |
61 void TorrentsPageListModel::DownloadTorrents(QItemSelection selection) | |
62 { | |
59 const auto indexes = selection.indexes(); | 63 const auto indexes = selection.indexes(); |
60 | 64 |
61 for (const auto& index : indexes) { | 65 for (const auto &index : indexes) { |
62 /* a torrent file IS literally text... */ | 66 /* a torrent file IS literally text... */ |
63 const std::string link = list.at(index.row()).GetLink(); | 67 const std::string link = list.at(index.row()).GetLink(); |
64 const std::string filename = list.at(index.row()).GetFilename() + ".torrent"; | 68 const std::string filename = list.at(index.row()).GetFilename() + ".torrent"; |
65 | 69 |
66 const std::filesystem::path torrents_dir = Filesystem::GetTorrentsPath(); | 70 const std::filesystem::path torrents_dir = Filesystem::GetTorrentsPath(); |
67 std::filesystem::create_directories(torrents_dir); | 71 std::filesystem::create_directories(torrents_dir); |
68 | 72 |
69 /* this sucks */ | 73 /* this sucks */ |
70 HTTP::RequestThread* thread = new HTTP::RequestThread(link, {}, "", HTTP::Type::Get, this); | 74 HTTP::RequestThread *thread = new HTTP::RequestThread(link, {}, "", HTTP::Type::Get, this); |
71 | 75 |
72 connect(thread, &HTTP::RequestThread::ReceivedData, this, [this, torrents_dir, filename](const QByteArray& data) { | 76 connect(thread, &HTTP::RequestThread::ReceivedData, this, |
73 std::ofstream file(torrents_dir / filename, std::ofstream::out | std::ofstream::trunc); | 77 [this, torrents_dir, filename](const QByteArray &data) { |
74 if (!file) | 78 std::ofstream file(torrents_dir / filename, std::ofstream::out | std::ofstream::trunc); |
75 return; // wat | 79 if (!file) |
76 | 80 return; // wat |
77 file.write(data.data(), data.size()); | 81 |
78 file.close(); | 82 file.write(data.data(), data.size()); |
79 }); | 83 file.close(); |
84 }); | |
80 connect(thread, &HTTP::RequestThread::finished, thread, &HTTP::RequestThread::deleteLater); | 85 connect(thread, &HTTP::RequestThread::finished, thread, &HTTP::RequestThread::deleteLater); |
81 | 86 |
82 thread->start(); | 87 thread->start(); |
83 } | 88 } |
84 } | 89 } |
85 | 90 |
86 QByteArray TorrentsPageListModel::DownloadTorrentList() { | 91 QByteArray TorrentsPageListModel::DownloadTorrentList() |
92 { | |
87 return HTTP::Request(session.config.torrents.feed_link); | 93 return HTTP::Request(session.config.torrents.feed_link); |
88 } | 94 } |
89 | 95 |
90 void TorrentsPageListModel::ParseFeedDescription(const std::string& description, Torrent& torrent) { | 96 void TorrentsPageListModel::ParseFeedDescription(const std::string &description, Torrent &torrent) |
97 { | |
91 /* Parse description... */ | 98 /* Parse description... */ |
92 enum class Keys { | 99 enum class Keys { |
93 SIZE, | 100 SIZE, |
94 AUTHORIZED, | 101 AUTHORIZED, |
95 SUBMITTER, | 102 SUBMITTER, |
124 default: break; | 131 default: break; |
125 } | 132 } |
126 } | 133 } |
127 } | 134 } |
128 | 135 |
129 void TorrentsPageListModel::ParseTorrentList(const QByteArray& ba) { | 136 void TorrentsPageListModel::ParseTorrentList(const QByteArray &ba) |
137 { | |
130 QDomDocument doc; | 138 QDomDocument doc; |
131 QDomNode node; | 139 QDomNode node; |
132 QDomNodeList node_nodes; | 140 QDomNodeList node_nodes; |
133 { | 141 { |
134 QString err; | 142 QString err; |
135 int err_ln; | 143 int err_ln; |
136 int err_col; | 144 int err_col; |
137 | 145 |
138 if (!doc.setContent(ba, &err, &err_ln, &err_col)) { | 146 if (!doc.setContent(ba, &err, &err_ln, &err_col)) { |
139 session.SetStatusBar(Strings::ToUtf8String(tr("Torrents: Failed to parse XML with error %1 at line %2, column %3").arg(err, QString::number(err_ln), QString::number(err_col)))); | 147 session.SetStatusBar( |
148 Strings::ToUtf8String(tr("Torrents: Failed to parse XML with error %1 at line %2, column %3") | |
149 .arg(err, QString::number(err_ln), QString::number(err_col)))); | |
140 return; // peace out | 150 return; // peace out |
141 } | 151 } |
142 } | 152 } |
143 | 153 |
144 /* my extra special dumb hack. */ | 154 /* my extra special dumb hack. */ |
151 | 161 |
152 list.clear(); | 162 list.clear(); |
153 | 163 |
154 node = doc; | 164 node = doc; |
155 | 165 |
156 for (const auto& n : {"rss", "channel"}) { | 166 for (const auto &n : {"rss", "channel"}) { |
157 node = node.namedItem(n); | 167 node = node.namedItem(n); |
158 if (node.isNull()) { std::cout << n << std::endl; goto end; } | 168 if (node.isNull()) { |
159 } | 169 std::cout << n << std::endl; |
160 | 170 goto end; |
161 if (!node.hasChildNodes()) { std::cout << "no child nodes" << std::endl; goto end; } | 171 } |
172 } | |
173 | |
174 if (!node.hasChildNodes()) { | |
175 std::cout << "no child nodes" << std::endl; | |
176 goto end; | |
177 } | |
162 | 178 |
163 node_nodes = node.childNodes(); | 179 node_nodes = node.childNodes(); |
164 | 180 |
165 for (int c = 0; c < node_nodes.count(); c++) { | 181 for (int c = 0; c < node_nodes.count(); c++) { |
166 const QDomNode item = node_nodes.at(c); | 182 const QDomNode item = node_nodes.at(c); |
167 if (!item.isElement() || item.nodeName() != "item") | 183 if (!item.isElement() || item.nodeName() != "item") |
168 continue; | 184 continue; |
169 | 185 |
170 const QDomNode title = item.namedItem("title"); | 186 const QDomNode title = item.namedItem("title"); |
171 if (!title.isElement()) continue; | 187 if (!title.isElement()) |
188 continue; | |
172 const QDomNode description = item.namedItem("description"); | 189 const QDomNode description = item.namedItem("description"); |
173 if (!description.isElement()) continue; | 190 if (!description.isElement()) |
191 continue; | |
174 const QDomNode link = item.namedItem("link"); | 192 const QDomNode link = item.namedItem("link"); |
175 if (!link.isElement()) continue; | 193 if (!link.isElement()) |
194 continue; | |
176 const QDomNode guid = item.namedItem("guid"); | 195 const QDomNode guid = item.namedItem("guid"); |
177 if (!guid.isElement()) continue; | 196 if (!guid.isElement()) |
197 continue; | |
178 const QDomNode pubDate = item.namedItem("pubDate"); | 198 const QDomNode pubDate = item.namedItem("pubDate"); |
179 if (!pubDate.isElement()) continue; | 199 if (!pubDate.isElement()) |
200 continue; | |
180 | 201 |
181 TorrentModelItem torrent; | 202 TorrentModelItem torrent; |
182 torrent.SetFilename(Strings::ToUtf8String(title.toElement().text())); /* "title" == filename */ | 203 torrent.SetFilename(Strings::ToUtf8String(title.toElement().text())); /* "title" == filename */ |
183 { | 204 { |
184 anitomy::Anitomy anitomy; | 205 anitomy::Anitomy anitomy; |
185 anitomy.Parse(torrent.GetFilename()); | 206 anitomy.Parse(torrent.GetFilename()); |
186 | 207 |
187 const auto& elements = anitomy.elements(); | 208 const auto &elements = anitomy.elements(); |
188 | 209 |
189 /* todo: patch Anitomy so that it doesn't use wide strings */ | 210 /* todo: patch Anitomy so that it doesn't use wide strings */ |
190 torrent.SetTitle(Strings::ToUtf8String(elements.get(anitomy::kElementAnimeTitle))); | 211 torrent.SetTitle(Strings::ToUtf8String(elements.get(anitomy::kElementAnimeTitle))); |
191 std::string episode = Strings::ToUtf8String(elements.get(anitomy::kElementEpisodeNumber)); | 212 std::string episode = Strings::ToUtf8String(elements.get(anitomy::kElementEpisodeNumber)); |
192 Strings::RemoveLeadingChars(episode, '0'); | 213 Strings::RemoveLeadingChars(episode, '0'); |
207 | 228 |
208 end: | 229 end: |
209 endResetModel(); | 230 endResetModel(); |
210 } | 231 } |
211 | 232 |
212 void TorrentsPageListModel::RefreshTorrentList() { | 233 void TorrentsPageListModel::RefreshTorrentList() |
234 { | |
213 ParseTorrentList(DownloadTorrentList()); | 235 ParseTorrentList(DownloadTorrentList()); |
214 } | 236 } |
215 | 237 |
216 int TorrentsPageListModel::rowCount(const QModelIndex& parent) const { | 238 int TorrentsPageListModel::rowCount(const QModelIndex &parent) const |
239 { | |
217 return list.size(); | 240 return list.size(); |
218 (void)(parent); | 241 (void)(parent); |
219 } | 242 } |
220 | 243 |
221 int TorrentsPageListModel::columnCount(const QModelIndex& parent) const { | 244 int TorrentsPageListModel::columnCount(const QModelIndex &parent) const |
245 { | |
222 return NB_COLUMNS; | 246 return NB_COLUMNS; |
223 (void)(parent); | 247 (void)(parent); |
224 } | 248 } |
225 | 249 |
226 QVariant TorrentsPageListModel::headerData(const int section, const Qt::Orientation orientation, const int role) const { | 250 QVariant TorrentsPageListModel::headerData(const int section, const Qt::Orientation orientation, const int role) const |
251 { | |
227 switch (role) { | 252 switch (role) { |
228 case Qt::DisplayRole: { | 253 case Qt::DisplayRole: { |
229 switch (section) { | 254 switch (section) { |
230 case TL_TITLE: return tr("Anime title"); | 255 case TL_TITLE: return tr("Anime title"); |
231 case TL_EPISODE: return tr("Episode"); | 256 case TL_EPISODE: return tr("Episode"); |
261 } | 286 } |
262 } | 287 } |
263 return QAbstractListModel::headerData(section, orientation, role); | 288 return QAbstractListModel::headerData(section, orientation, role); |
264 } | 289 } |
265 | 290 |
266 bool TorrentsPageListModel::setData(const QModelIndex& index, const QVariant& value, int role) { | 291 bool TorrentsPageListModel::setData(const QModelIndex &index, const QVariant &value, int role) |
267 TorrentModelItem& item = list.at(index.row()); | 292 { |
293 TorrentModelItem &item = list.at(index.row()); | |
268 | 294 |
269 if (index.column() == 0) { | 295 if (index.column() == 0) { |
270 switch (role) { | 296 switch (role) { |
271 case Qt::EditRole: return false; | 297 case Qt::EditRole: return false; |
272 case Qt::CheckStateRole: | 298 case Qt::CheckStateRole: |
277 } | 303 } |
278 | 304 |
279 return QAbstractItemModel::setData(index, value, role); | 305 return QAbstractItemModel::setData(index, value, role); |
280 } | 306 } |
281 | 307 |
282 QVariant TorrentsPageListModel::data(const QModelIndex& index, int role) const { | 308 QVariant TorrentsPageListModel::data(const QModelIndex &index, int role) const |
309 { | |
283 if (!index.isValid()) | 310 if (!index.isValid()) |
284 return QVariant(); | 311 return QVariant(); |
285 | 312 |
286 const TorrentModelItem& item = list.at(index.row()); | 313 const TorrentModelItem &item = list.at(index.row()); |
287 | 314 |
288 switch (role) { | 315 switch (role) { |
289 case Qt::DisplayRole: | 316 case Qt::DisplayRole: |
290 switch (index.column()) { | 317 switch (index.column()) { |
291 case TL_TITLE: return Strings::ToQString(item.GetTitle()); | 318 case TL_TITLE: return Strings::ToQString(item.GetTitle()); |
342 break; | 369 break; |
343 } | 370 } |
344 return QVariant(); | 371 return QVariant(); |
345 } | 372 } |
346 | 373 |
347 Qt::ItemFlags TorrentsPageListModel::flags(const QModelIndex& index) const { | 374 Qt::ItemFlags TorrentsPageListModel::flags(const QModelIndex &index) const |
375 { | |
348 if (!index.isValid()) | 376 if (!index.isValid()) |
349 return Qt::NoItemFlags; | 377 return Qt::NoItemFlags; |
350 | 378 |
351 return Qt::ItemIsEnabled | Qt::ItemIsSelectable; | 379 return Qt::ItemIsEnabled | Qt::ItemIsSelectable; |
352 } | 380 } |
353 | 381 |
354 TorrentsPage::TorrentsPage(QWidget* parent) : QFrame(parent) { | 382 TorrentsPage::TorrentsPage(QWidget *parent) : QFrame(parent) |
383 { | |
355 setFrameShape(QFrame::Box); | 384 setFrameShape(QFrame::Box); |
356 setFrameShadow(QFrame::Sunken); | 385 setFrameShadow(QFrame::Sunken); |
357 | 386 |
358 QVBoxLayout* layout = new QVBoxLayout(this); | 387 QVBoxLayout *layout = new QVBoxLayout(this); |
359 layout->setContentsMargins(0, 0, 0, 0); | 388 layout->setContentsMargins(0, 0, 0, 0); |
360 layout->setSpacing(0); | 389 layout->setSpacing(0); |
361 | 390 |
362 { | 391 { |
363 /* Toolbar */ | 392 /* Toolbar */ |
364 QToolBar* toolbar = new QToolBar(this); | 393 QToolBar *toolbar = new QToolBar(this); |
365 toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); | 394 toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); |
366 toolbar->setIconSize(QSize(16, 16)); | 395 toolbar->setIconSize(QSize(16, 16)); |
367 toolbar->setMovable(false); | 396 toolbar->setMovable(false); |
368 | 397 |
369 { | 398 { |
378 { | 407 { |
379 toolbar->addAction(QIcon(":/icons/16x16/navigation-270-button.png"), tr("Download &marked torrents"), | 408 toolbar->addAction(QIcon(":/icons/16x16/navigation-270-button.png"), tr("Download &marked torrents"), |
380 [this] { DownloadSelection(); }); | 409 [this] { DownloadSelection(); }); |
381 } | 410 } |
382 | 411 |
383 { toolbar->addAction(QIcon(":/icons/16x16/cross-button.png"), tr("&Discard all")); } | 412 { |
413 toolbar->addAction(QIcon(":/icons/16x16/cross-button.png"), tr("&Discard all")); | |
414 } | |
384 | 415 |
385 toolbar->addSeparator(); | 416 toolbar->addSeparator(); |
386 | 417 |
387 { toolbar->addAction(QIcon(":/icons/16x16/gear.png"), tr("&Settings")); } | 418 { |
419 toolbar->addAction(QIcon(":/icons/16x16/gear.png"), tr("&Settings")); | |
420 } | |
388 | 421 |
389 layout->addWidget(toolbar); | 422 layout->addWidget(toolbar); |
390 } | 423 } |
391 | 424 |
392 { | 425 { |
393 QFrame* line = new QFrame(this); | 426 QFrame *line = new QFrame(this); |
394 line->setFrameShape(QFrame::HLine); | 427 line->setFrameShape(QFrame::HLine); |
395 line->setFrameShadow(QFrame::Sunken); | 428 line->setFrameShadow(QFrame::Sunken); |
396 line->setLineWidth(1); | 429 line->setLineWidth(1); |
397 layout->addWidget(line); | 430 layout->addWidget(line); |
398 } | 431 } |
435 | 468 |
436 layout->addWidget(treeview); | 469 layout->addWidget(treeview); |
437 } | 470 } |
438 } | 471 } |
439 | 472 |
440 void TorrentsPage::DownloadSelection() { | 473 void TorrentsPage::DownloadSelection() |
474 { | |
441 if (!model) | 475 if (!model) |
442 return; | 476 return; |
443 | 477 |
444 const QItemSelection selection = sort_model->mapSelectionToSource(treeview->selectionModel()->selection()); | 478 const QItemSelection selection = sort_model->mapSelectionToSource(treeview->selectionModel()->selection()); |
445 | 479 |
446 model->DownloadTorrents(selection); | 480 model->DownloadTorrents(selection); |
447 } | 481 } |
448 | 482 |
449 void TorrentsPage::Refresh() { | 483 void TorrentsPage::Refresh() |
484 { | |
450 if (!model) | 485 if (!model) |
451 return; | 486 return; |
452 | 487 |
453 HTTP::RequestThread* thread = new HTTP::RequestThread(session.config.torrents.feed_link); | 488 HTTP::RequestThread *thread = new HTTP::RequestThread(session.config.torrents.feed_link); |
454 | 489 |
455 connect(thread, &HTTP::RequestThread::ReceivedData, this, [&](const QByteArray& ba) { | 490 connect(thread, &HTTP::RequestThread::ReceivedData, this, [&](const QByteArray &ba) { |
456 /* This is to make sure we aren't in a different thread | 491 /* This is to make sure we aren't in a different thread |
457 * messing around with GUI stuff | 492 * messing around with GUI stuff |
458 */ | 493 */ |
459 treeview->setUpdatesEnabled(false); | 494 treeview->setUpdatesEnabled(false); |
460 model->ParseTorrentList(ba); | 495 model->ParseTorrentList(ba); |