#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() { auto pid = fork(); if(pid != 0) { qDebug() << "Process id: " << getpid() << "forked."; return pid; } qDebug() << "Process id: " << getpid() << "created for torrent session with download path: " << download_path.string(); manager = new SocketManager(); socket_manager_thread = new QThread(this); manager->moveToThread(socket_manager_thread); socket_manager_thread->start(); connect(this, &TorrentSession::startSocketManager, manager, &SocketManager::start); connect(this, &TorrentSession::sendStatusSignalToSocketManager, manager, &SocketManager::statusSignalFromTorrentSession); connect(manager, &SocketManager::status, this, &TorrentSession::sendStatus); connect(manager, &SocketManager::pause, this, &TorrentSession::pause); connect(manager, &SocketManager::resume, this, &TorrentSession::resume); connect(manager, &SocketManager::remove, this, &TorrentSession::remove); emit startSocketManager(); connect(&start_timer, &QTimer::timeout, this, &TorrentSession::launchSession); start_timer.setInterval(300); start_timer.setSingleShot(true); start_timer.start(); return pid; } void TorrentSession::sendStatus() { } void TorrentSession::pause() { } void TorrentSession::resume() { } 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; } } } SocketManager::SocketManager(QObject *parent) { } void SocketManager::start() { server = new QLocalServer(this); connect(server, &QLocalServer::newConnection, this, &SocketManager::addConnection); auto unique_path = testDir("/tmp/nekodl", ".sock"); bool f = server->listen(QString::fromStdString(unique_path.string())); if(!f) { qDebug() << "Failed to start server"; } } void SocketManager::statusSignalFromTorrentSession(QByteArray data) { if(socket && socket->isOpen() && socket->isValid()) { socket->write(data); socket->flush(); } } void SocketManager::addConnection() { if(server->hasPendingConnections()) { if(socket) { socket->deleteLater(); } socket = server->nextPendingConnection(); connect(socket, &QLocalSocket::readyRead, this, &SocketManager::commandFromClient); } } void SocketManager::commandFromClient() { if(socket) { QByteArray big_data; while(socket->bytesAvailable()) { auto data = socket->readAll(); big_data += data; } QString data(big_data); sendSignalToTorrentSession(data); } } void SocketManager::sendSignalToTorrentSession(QString command) { if(command == "status") { emit status(); } if(command == "pause") { emit pause(); } if(command == "resume") { emit resume(); } if(command == "remove") { emit remove(); } } std::string SocketManager::numToStr(int num) { std::stringstream ss; ss << num; return ss.str(); } std::filesystem::path SocketManager::testDir(std::string file_name, std::string file_format) { int i = 1; while(true) { std::filesystem::path final_path = std::filesystem::path(file_name + "_" + numToStr(i++) + file_format); qDebug() << "Checking path: " << final_path.string(); if(!std::filesystem::exists(final_path)) { return final_path; } } }