diff --git a/mainscreen.cpp b/mainscreen.cpp index a0b3fcf..93e8ea6 100644 --- a/mainscreen.cpp +++ b/mainscreen.cpp @@ -33,6 +33,18 @@ MainScreen::MainScreen(std::string search_term, QObject *parent) } } +void MainScreen::resumeOldTorrentSession(SearchItem it) +{ + qDebug() << "Resuming session: " << it.title; + qDebug() << "Old download path: " << it.download_path.string(); + TorrentSession *session = new TorrentSession(it, it.download_path); + auto pid = session->start(); + if(pid != 0) { + session->deleteLater(); + qDebug() << "Parent done."; + } +} + void MainScreen::renderSearchPage(std::string search_term) { if(!manager) { @@ -53,6 +65,15 @@ void MainScreen::renderSearchPage(std::string search_term) void MainScreen::renderHomePage() { qDebug() << "Render Home Page"; + if(!socket) { + socket = new Socket(); + socket_thread = new QThread(this); + socket->moveToThread(socket_thread); + connect(this, &MainScreen::startSocket, socket, &Socket::start); + connect(socket, &Socket::launchTorrentSession, this, &MainScreen::resumeOldTorrentSession); + socket_thread->start(); + emit startSocket(); + } } std::vector MainScreen::parseSearchPage(std::string html_page) @@ -198,6 +219,7 @@ int MainScreen::startDownloadIndex(std::vector res, int index) return -1; } auto download_path = expected_path.value(); + res[index].download_path = download_path; TorrentSession *session = new TorrentSession(res[index], download_path); auto pid = session->start(); if(pid != 0) { @@ -211,6 +233,7 @@ std::expected MainScreen::runBasicShell(SearchItem it) { qDebug() << "Running Basic Shell"; qDebug() << "Valid commands: ls, cd, select, pwd, rmdir, mkdir, set, lstags and exit."; + qDebug() << "mkdir: Use $title to create a folder with name of title."; qDebug() << "select: Selects the present working dir" " as download directory if no arguments are provided." "Else uses the provided argument as a tag or a path."; @@ -218,8 +241,10 @@ std::expected MainScreen::runBasicShell(SearchItem it) qDebug() << "set: used for setting a tag. set [TAG_NAME] [FOLDER PATH]"; qDebug() << "lstags: Lists all tags stored."; namespace fs = std::filesystem; + std::string extras = ""; while(true) { - std::print("nsh|{}>",fs::current_path()); + std::print("nsh|{}>{}",fs::current_path(), extras); + extras = ""; std::string command; std::getline(std::cin, command); if(command == "ls") { @@ -319,10 +344,16 @@ std::expected MainScreen::runBasicShell(SearchItem it) } if(command.starts_with("mkdir")) { auto arg = stripForArgument(command, 1); - fs::path path_to_create = fs::current_path() / fs::path(arg); - bool t = fs::create_directory(path_to_create); - if(!t) { - qDebug() << "Failed to create path: " << QString::fromStdString(path_to_create); + if(!arg.starts_with('$')) { + fs::path path_to_create = fs::current_path() / fs::path(arg); + bool t = fs::create_directory(path_to_create); + if(!t) { + qDebug() << "Failed to create path: " << QString::fromStdString(path_to_create); + } + } + else if(arg == "$title") { + extras = "mkdir " + it.title.toStdString(); + continue; } } } @@ -343,6 +374,11 @@ std::string MainScreen::stripForArgument(std::string command, int index) return ""; } +void MainScreen::statusFromSocket(Status st) +{ + +} + void MainScreen::renderSearchPage_(QNetworkReply *rep) { QByteArray data = rep->readAll(); @@ -381,6 +417,7 @@ void SearchItem::printData() qDebug() << "Downloads: " << downloads; qDebug() << "Timestamp: " << timestamp; qDebug() << "Magnet: " << magnet; + qDebug() << "Download path: " << download_path.string(); } void SearchItem::writeData(std::string file_path) @@ -390,9 +427,14 @@ void SearchItem::writeData(std::string file_path) qDebug() << "Failed to write to file: " << file_path; return; } + qDebug() << "Writing title: " << title; + qDebug() << "Writing url: " << url; + qDebug() << "Writing magnet: " << magnet; + qDebug() << "Writing download_path: " << download_path.string(); out << title.toStdString() << std::endl; out << url.toDisplayString().toStdString() << std::endl; out << magnet.toStdString() << std::endl; + out << download_path.string() << std::endl; out.flush(); out.close(); } @@ -404,14 +446,16 @@ void SearchItem::readFromData(std::string file_path) qDebug() << "Failed to read from file: " << file_path; return; } - std::string stitle, surl, smagnet; - in >> stitle; - in >> surl; - in >> smagnet; + std::string stitle, surl, smagnet, spath; + std::getline(in, stitle); + std::getline(in, surl); + std::getline(in, smagnet); + std::getline(in, spath); in.close(); title = QString::fromStdString(stitle); url = QUrl(QString::fromStdString(surl)); magnet = QString::fromStdString(smagnet); + download_path = std::filesystem::canonical(spath); in.close(); } @@ -470,3 +514,128 @@ std::unordered_map MainScreen::Tags::getAllTags(std::s } return t; } + +Socket::Socket(QObject *parent) +{ + +} + + +//For each resume file found either start the libtorrent session if it was not running or +//Ask each session their status. +void Socket::start() +{ + // Generate a list of all active libtorrent sessions. + namespace fs = std::filesystem; + auto temp_dir = QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString(); + auto temp_dir_path = fs::path(temp_dir); + fs::directory_iterator itr(temp_dir_path); + for(auto &file_folder: itr) { + if(file_folder.is_regular_file() || file_folder.is_socket()) { + auto file_name = file_folder.path().string(); + if(file_name.contains("nekodl") + && file_name.ends_with(".sock")) { + QLocalSocket *socket = new QLocalSocket(this); + connect(socket, &QLocalSocket::readyRead, this, &Socket::socketReadyRead); + socket->connectToServer(QString::fromStdString(file_name)); + qDebug() << "Opening file: " << file_name; + if(socket->isOpen()) { + qDebug() << "Open socket: " << file_name; + QString data = ""; + socket->write(data.toUtf8()); + socket->flush(); + active_sockets[file_name] = socket; + } + else { + qDebug() << "Stale socket: " << file_name; + fs::remove(file_name); + disconnect(socket); + socket->deleteLater(); + } + } + } + } + qDebug() << "Checking temp dir."; + if(!recheck) { + if(!timer) { + timer = new QTimer(this); + timer->setInterval(5000); + timer->setSingleShot(true); + connect(timer, &QTimer::timeout, this, [&](){ + startNotDownloadingSessions(); + }); + timer->start(); + } + + + if(checked_resume_files) { + if(timer) { + timer->deleteLater(); + } + } + recheck = new QTimer(this); + recheck->setInterval(3000); + recheck->setSingleShot(false); + connect(recheck, &QTimer::timeout, this, [this](){ + start(); + }); + recheck->start(); + } +} + +// Start all sessions with a .resume_file but +// are not in list of active libtorrent session. +void Socket::startNotDownloadingSessions() +{ + namespace fs = std::filesystem; + fs::path resume_file_location = fs::path( + QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation).toStdString() + ); + resume_file_location = resume_file_location / "resumes"; + if(!fs::is_directory(resume_file_location)) { + fs::create_directories(resume_file_location); + } + fs::directory_iterator itr(resume_file_location); + for(auto &file_folder: itr) { + + qDebug() << "Checking: " << file_folder.path().string(); + if(fs::is_regular_file(file_folder)) { + if(file_folder.path().extension() == ".resume_file") { + //Load resume file. + qDebug() << "Resume file found: " << file_folder.path().string(); + auto parent_path = file_folder.path().parent_path(); + auto file_name = parent_path / file_folder.path().stem(); + auto searchitem_file_path = file_name.string() + ".searchitem"; + qDebug() << parent_path.string() << ": " << file_name.string(); + qDebug() << "Search item path: " << searchitem_file_path; + if(fs::exists(searchitem_file_path)) + { + SearchItem it; + it.readFromData(searchitem_file_path); + qDebug() << "Recovering from: " << it.title; + if(!isTitleInActiveSession(it.title)) { + qDebug() << "Title not in active session list."; + it.printData(); + emit launchTorrentSession(it); + } + } + else { + qDebug() << "Failed to recover: " << searchitem_file_path; + } + } + } + } + checked_resume_files = true; + +} + +bool Socket::isTitleInActiveSession(QString title) +{ + return active_sockets.find(title.toStdString()) != active_sockets.end(); +} + + +void Socket::socketReadyRead() +{ + +} diff --git a/mainscreen.h b/mainscreen.h index b007906..2f60168 100644 --- a/mainscreen.h +++ b/mainscreen.h @@ -29,11 +29,37 @@ struct SearchItem { QString downloads; QString timestamp; QString magnet; + std::filesystem::path download_path; void printData(); void writeData(std::string file_path); void readFromData(std::string file_path); }; +struct Status { + SearchItem it; + int progress; + float speed; +}; + +class Socket: public QObject { + Q_OBJECT + std::unordered_map active_sockets; + QTimer *recheck = nullptr; + QTimer *timer = nullptr; + bool checked_resume_files = false; +public: + Socket(QObject *parent = nullptr); +signals: + void launchTorrentSession(SearchItem it); + void sendStatusToMainScreen(Status st); +public slots: + void start(); + void startNotDownloadingSessions(); +private slots: + bool isTitleInActiveSession(QString title); + void socketReadyRead(); +}; + class MainScreen : public QObject { Q_OBJECT @@ -52,9 +78,15 @@ class MainScreen : public QObject QNetworkAccessManager *manager = nullptr; std::string tags_file_path = ""; + Socket *socket = nullptr; + QThread *socket_thread = nullptr; public: explicit MainScreen(std::string search_term = "", QObject *parent = nullptr); signals: + void startSocket(); +public slots: + void resumeOldTorrentSession(SearchItem it); + void statusFromSocket(Status st); private: void renderSearchPage(std::string search_term); void renderHomePage(); diff --git a/torrentsession.cpp b/torrentsession.cpp index 7aa7dcc..b7828b3 100644 --- a/torrentsession.cpp +++ b/torrentsession.cpp @@ -94,9 +94,13 @@ void TorrentSession::launchSession() 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"); - + auto search_item_path = (fs::path(resume_files_location) / + fs::path(item->title.toStdString())).string() + ".searchitem"; + if(!std::filesystem::exists(search_item_path)) + item->writeData((fs::path(resume_files_location) / + fs::path(item->title.toStdString())).string() + ".searchitem"); + else + qDebug() << ".seachitem for: " << item->title.toStdString() << " already exists."; // 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) / @@ -108,9 +112,14 @@ void TorrentSession::launchSession() lt::add_torrent_params magnet = lt::parse_magnet_uri(item->magnet.toStdString()); if (buf.size()) { + qDebug() << "Loading info from resume file."; lt::add_torrent_params atp = lt::read_resume_data(buf); - if (atp.info_hashes == magnet.info_hashes) magnet = std::move(atp); + if (atp.info_hashes == magnet.info_hashes) { + magnet = std::move(atp); + qDebug() << "Magnet hashes matched."; + } } + qDebug() << "Download path: " << download_path.string(); magnet.save_path = download_path.string(); // save location ses.async_add_torrent(std::move(magnet)); @@ -144,7 +153,6 @@ void TorrentSession::launchSession() 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); @@ -179,8 +187,8 @@ void TorrentSession::launchSession() 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)) { + // save resume data once every 3 seconds + if (clk::now() - last_save_resume > std::chrono::seconds(3)) { h.save_resume_data(lt::torrent_handle::only_if_modified | lt::torrent_handle::save_info_dict); last_save_resume = clk::now(); diff --git a/torrentsession.h b/torrentsession.h index 8dd5645..77427ba 100644 --- a/torrentsession.h +++ b/torrentsession.h @@ -47,6 +47,8 @@ class TorrentSession : public QObject enum class Status { Downloading, Paused, + Finished, + Seeding, }; SocketManager *manager = nullptr; QThread *socket_manager_thread = nullptr;