diff --git a/CMakeLists.txt b/CMakeLists.txt index bc0bf7c..3238ca9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,8 @@ FetchContent_MakeAvailable(htmlparser) find_package(Qt6 6.5 REQUIRED COMPONENTS Core Network) find_package(Boost CONFIG REQUIRED) find_package(OpenSSL REQUIRED) +find_package(PkgConfig REQUIRED) +pkg_check_modules(LibTorrent REQUIRED IMPORTED_TARGET libtorrent-rasterbar) qt_standard_project_setup() @@ -37,7 +39,7 @@ target_link_libraries(nekodl PRIVATE Qt::Core Qt::Network - libtorrent.so + PkgConfig::LibTorrent ftxui::ftxui htmlparser OpenSSL::SSL diff --git a/mainscreen.cpp b/mainscreen.cpp index 76e02b7..a0b3fcf 100644 --- a/mainscreen.cpp +++ b/mainscreen.cpp @@ -383,6 +383,38 @@ void SearchItem::printData() qDebug() << "Magnet: " << magnet; } +void SearchItem::writeData(std::string file_path) +{ + std::fstream out(file_path, std::fstream::out); + if(!out.is_open()) { + qDebug() << "Failed to write to file: " << file_path; + return; + } + out << title.toStdString() << std::endl; + out << url.toDisplayString().toStdString() << std::endl; + out << magnet.toStdString() << std::endl; + out.flush(); + out.close(); +} + +void SearchItem::readFromData(std::string file_path) +{ + std::fstream in(file_path, std::fstream::in); + if(!in.is_open()) { + qDebug() << "Failed to read from file: " << file_path; + return; + } + std::string stitle, surl, smagnet; + in >> stitle; + in >> surl; + in >> smagnet; + in.close(); + title = QString::fromStdString(stitle); + url = QUrl(QString::fromStdString(surl)); + magnet = QString::fromStdString(smagnet); + in.close(); +} + std::string MainScreen::Tags::getTag(std::string file_name, std::string key) { QJsonDocument doc = QJsonDocument::fromJson(readFile(file_name)); diff --git a/mainscreen.h b/mainscreen.h index 96a7d28..b007906 100644 --- a/mainscreen.h +++ b/mainscreen.h @@ -30,6 +30,8 @@ struct SearchItem { QString timestamp; QString magnet; void printData(); + void writeData(std::string file_path); + void readFromData(std::string file_path); }; class MainScreen : public QObject diff --git a/torrentsession.cpp b/torrentsession.cpp index 4f485ef..7aa7dcc 100644 --- a/torrentsession.cpp +++ b/torrentsession.cpp @@ -1,15 +1,26 @@ #include "torrentsession.h" #include "mainscreen.h" +#include #include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include TorrentSession::TorrentSession(SearchItem it, std::filesystem::path download_path, QObject *parent) { this->item = new SearchItem(it); this->download_path = download_path; - - - } int TorrentSession::start() @@ -64,7 +75,121 @@ void TorrentSession::remove() //libtorrent stuff starts here. void TorrentSession::launchSession() { + using clk = std::chrono::steady_clock; + namespace fs = std::filesystem; + lt::settings_pack pack; + pack.set_int(lt::settings_pack::alert_mask + , lt::alert_category::error + | lt::alert_category::storage + | lt::alert_category::status + | lt::alert_category::piece_progress + ); + lt::session ses(pack); + clk::time_point last_save_resume = clk::now(); + std::string resume_files_location = fs::path( + QStandardPaths::writableLocation( + QStandardPaths::AppLocalDataLocation + ).toStdString()) / "resumes"; + if(!std::filesystem::exists(resume_files_location)) { + std::filesystem::create_directories(resume_files_location); + } + item->writeData((fs::path(resume_files_location) / + fs::path(item->title.toStdString())).string() + ".searchitem"); + + + // load resume data from disk and pass it in as we add the magnet link + std::string resume_files_name = fs::path(resume_files_location) / + fs::path(item->title.toStdString() + ".resume_file"); + std::ifstream ifs(resume_files_name, std::ios_base::binary); + ifs.unsetf(std::ios_base::skipws); + std::vector buf{std::istream_iterator(ifs) + , std::istream_iterator()}; + + lt::add_torrent_params magnet = lt::parse_magnet_uri(item->magnet.toStdString()); + if (buf.size()) { + lt::add_torrent_params atp = lt::read_resume_data(buf); + if (atp.info_hashes == magnet.info_hashes) magnet = std::move(atp); + } + magnet.save_path = download_path.string(); // save location + + ses.async_add_torrent(std::move(magnet)); + lt::torrent_handle h; + + // set when we're exiting + bool done = false; + bool errored = false; + bool finished = false; + std::string errored_message = ""; + for (;;) { + std::vector alerts; + ses.pop_alerts(&alerts); + bool quit_loop = false; + for (lt::alert const* a : alerts) { + if (auto at = lt::alert_cast(a)) { + h = at->handle; + } + // if we receive the finished alert or an error, we're done + if (lt::alert_cast(a)) { + h.save_resume_data(lt::torrent_handle::only_if_modified + | lt::torrent_handle::save_info_dict); + done = true; + finished = true; + } + if (lt::alert_cast(a)) { + done = true; + errored = true; + errored_message = a->message(); + std::cout << errored_message << std::endl; + h.save_resume_data(lt::torrent_handle::only_if_modified + | lt::torrent_handle::save_info_dict); + } + + // when resume data is ready, save it + if (auto rd = lt::alert_cast(a)) { + std::ofstream of(resume_files_name, std::ios_base::binary); + of.unsetf(std::ios_base::skipws); + auto const b = write_resume_data_buf(rd->params); + of.write(b.data(), int(b.size())); + if (done) { + quit_loop = true; + } + } + + if (lt::alert_cast(a)) { + if (done) { + quit_loop = true; + } + } + + if (auto st = lt::alert_cast(a)) { + if (st->status.empty()) continue; + + // we only have a single torrent, so we know which one + // the status is for + lt::torrent_status const& s = st->status[0]; + std::cout << '\r' << s.state << ' ' + << (s.download_payload_rate / 1000) << " kB/s " + << (s.total_done / 1000) << " kB (" + << (s.progress_ppm / 10000) << "%) downloaded (" + << s.num_peers << " peers)\x1b[K"; + std::cout.flush(); + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + ses.post_torrent_updates(); + QCoreApplication::processEvents(); + // save resume data once every 30 seconds + if (clk::now() - last_save_resume > std::chrono::seconds(30)) { + h.save_resume_data(lt::torrent_handle::only_if_modified + | lt::torrent_handle::save_info_dict); + last_save_resume = clk::now(); + } + if(quit_loop) { + qDebug() << "Done."; + break; + } + } } diff --git a/torrentsession.h b/torrentsession.h index 02a7ea7..8dd5645 100644 --- a/torrentsession.h +++ b/torrentsession.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include