commit 2fd0d6d3d4e88e9cb27aa1145f539eb993a81d03 Author: unrealdragon Date: Sat Aug 22 22:43:53 2026 +0530 Progress: 25%. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ca8b61e --- /dev/null +++ b/.gitignore @@ -0,0 +1,84 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash +**/.qmlls.ini + +# qtcreator generated files +*.pro.user* +*.qbs.user* +CMakeLists.txt.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + +# Directories with generated files +.moc/ +.obj/ +.pch/ +.rcc/ +.uic/ +/build*/ +/.qtcreator/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..bc0bf7c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.19) +project(nekodl LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 26) + +include(FetchContent) +FetchContent_Declare(ftxui + GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui + GIT_TAG v7.0.3 +) +FetchContent_MakeAvailable(ftxui) + +FetchContent_Declare(htmlparser + GIT_REPOSITORY https://github.com/mylogin/htmlparser + GIT_TAG f468bb3eb6ed2ac38b7c5cb8e6789346f888c69a +) +FetchContent_MakeAvailable(htmlparser) + +find_package(Qt6 6.5 REQUIRED COMPONENTS Core Network) +find_package(Boost CONFIG REQUIRED) +find_package(OpenSSL REQUIRED) + +qt_standard_project_setup() + +qt_add_executable(nekodl + main.cpp + mainscreen.h mainscreen.cpp + torrentsession.h torrentsession.cpp +) + +target_include_directories(nekodl + PRIVATE + ${htmlparser_SOURCE_DIR} +) + +target_link_libraries(nekodl + PRIVATE + Qt::Core + Qt::Network + libtorrent.so + ftxui::ftxui + htmlparser + OpenSSL::SSL + OpenSSL::Crypto +) + +target_compile_definitions(nekodl PRIVATE TORRENT_USE_OPENSSL=1) + +install(TARGETS nekodl + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..74b26af --- /dev/null +++ b/main.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include "mainscreen.h" + +std::expected parseArgs(int argc, char **argv) { + std::vector arg_list; + for(int i=1;i < argc; i++) { + arg_list.push_back(argv[i]); + } + if (argc == 1) { + return ""; + } + if(argc == 2) { + return arg_list[0]; + } + else { + return std::unexpected(1); + } +} + +void printHelp(int t) { + if(t == 1) { + std::cout << "Invalid number of arguments." << std::endl; + } + std::cout << "USAGE: nekodl [Search Term]" << std::endl; +} + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + auto args = parseArgs(argc, argv); + if(!args.has_value()) { + printHelp(args.error()); + return -1; + } + MainScreen screen(args.value()); + // Set up code that uses the Qt event loop here. + // Call QCoreApplication::quit() or QCoreApplication::exit() to quit the application. + // A not very useful example would be including + // #include + // near the top of the file and calling + // QTimer::singleShot(5000, &a, &QCoreApplication::quit); + // which quits the application after 5 seconds. + + // If you do not need a running Qt event loop, remove the call + // to QCoreApplication::exec() or use the Non-Qt Plain C++ Application template. + + return QCoreApplication::exec(); +} diff --git a/mainscreen.cpp b/mainscreen.cpp new file mode 100644 index 0000000..76e02b7 --- /dev/null +++ b/mainscreen.cpp @@ -0,0 +1,440 @@ +#include "mainscreen.h" +#include "html.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MainScreen::MainScreen(std::string search_term, QObject *parent) + : QObject{parent} +{ + tags_file_path = std::filesystem::path( + QStandardPaths::writableLocation + (QStandardPaths::AppLocalDataLocation).toStdString()); + if(!std::filesystem::exists(tags_file_path)) { + qDebug() << "Created dir."; + std::filesystem::create_directories(tags_file_path); + } + else { + qDebug() << "It already exists."; + } + tags_file_path = std::filesystem::path(tags_file_path) / "tags.json"; + qDebug() << "Using tags file location: " << tags_file_path; + if(search_term == "") { + renderHomePage(); + } + else { + renderSearchPage(search_term); + } +} + +void MainScreen::renderSearchPage(std::string search_term) +{ + if(!manager) { + manager = new QNetworkAccessManager(this); + } + disconnect(manager, nullptr, nullptr, nullptr); + QNetworkRequest req; + QUrl req_url; + req_url.setUrl("https://nyaa.si/"); + QUrlQuery Q{{"f","0"},{"c","0_0"},{"s", "seeders"},{"o","desc"}}; + Q.addQueryItem("q", QString::fromStdString(search_term)); + req_url.setQuery(Q); + req.setUrl(req_url); + connect(manager, &QNetworkAccessManager::finished, this, &MainScreen::renderSearchPage_); + manager->get(req); +} + +void MainScreen::renderHomePage() +{ + qDebug() << "Render Home Page"; +} + +std::vector MainScreen::parseSearchPage(std::string html_page) +{ + html::parser p; + auto root = p.parse(html_page); + qDebug() << root->size() << " elements."; + auto titles = root->select("div>table>tbody>tr"); + qDebug() << "Found rows: " << titles.size(); + std::vector results; + for(auto i: titles) { + /* + * title -> name I see in the selection. + * url -> url for it's nyaa page. + * seeders + * leachers + * downloads + * timestamp + * magnet + */ + + auto g = i->select("td>a[title]"); + SearchItem result; + for(auto j: g) { + if(j->has_attr("class") || j->select("img").size() > 0) { + continue; + } + result.title = QString::fromStdString(j->get_attr("title")); + result.url = QUrl(QString::fromStdString(j->get_attr("href"))); + } + g = i->select("a[href*='magnet:?']"); + if(g.size() == 0 || g.size() > 1) { + continue; + } + result.magnet = QString::fromStdString(g[0]->get_attr("href")); + // Asuming 4th column is size + // 5th is timestamp + // 6th is seeders + // 7th is leachers + // 8th is total downloads + g = i->select("td[data-timestamp]"); + if(g.size() > 0) + result.timestamp = QString::fromStdString(g[0]->get_attr("data-timestamp")); + g = i->select("td"); + if(g.size() >= 8) { + result.size = QString::fromStdString(g[3]->to_text()); + result.seeders = QString::fromStdString(g[5]->to_text()); + result.leechers = QString::fromStdString(g[6]->to_text()); + result.downloads = QString::fromStdString(g[7]->to_text()); + } + results.push_back(result); + } + return results; +} + +std::vector MainScreen::getStringEntries(std::vector res) +{ + std::vector dat; + for(auto &i: res) { + dat.push_back(i.title.toStdString()); + } + return dat; +} + +//SearchMenu is rendered here. +int MainScreen::displaySearchMenu(std::vector res) +{ + using namespace ftxui; + std::vector entries; + for (const auto& f : res) { + entries.push_back(f.title.toStdString()); + } + + int selected = 0; + bool exit_flag = false; + auto screen = ScreenInteractive::TerminalOutput(); + MenuOption option; + std::string status_message = "Use arrows to navigate. Press Enter to select, 'q' to quit."; + option.on_enter = [&] { + screen.Exit(); + status_message = "You selected: " + res[selected].title.toStdString(); + }; + option.entries_option.transform = [&res](const EntryState& state) { + const auto& file = res[state.index]; + auto row = hbox({ + text(file.title.toStdString()) | flex, + text(file.size.toStdString()) | size(WIDTH, EQUAL, 10), + text(file.timestamp.toStdString()) | size(WIDTH, EQUAL, 15), + text(file.seeders.toStdString()) | size(WIDTH, EQUAL, 10), + text(file.leechers.toStdString()) | size(WIDTH, EQUAL, 10), + text(file.downloads.toStdString()) | size(WIDTH, EQUAL, 15), + }); + if (state.active) { + row = row | inverted | bold; + } + + return row; + }; + auto menu = Menu(&entries, &selected, option); + auto renderer = Renderer(menu, [&] { + Element header = hbox({ + text(" TITLE") | flex, + text("SIZE") | size(WIDTH, EQUAL, 12), + text("DATE") | size(WIDTH, EQUAL, 12), + text("SEEDERS") | size(WIDTH, EQUAL, 12), + text("LEECHERS") | size(WIDTH, EQUAL, 12), + text("DOWNLOADS") | size(WIDTH, EQUAL, 12), + }) | underlined; + + return window(text(" Search Results "), + vbox({ + header, + separatorEmpty(), + menu->Render() + }) + ) | size(WIDTH, GREATER_THAN, 50); + }); + + + + auto container = CatchEvent(renderer, [&](Event event) { + // Checking for 'q', 'Q', or Escape + if (event == Event::Character("q") || event == Event::Character("Q") || event == Event::Escape) { + screen.Exit(); + exit_flag = true; + return true; + } + return false; + }); + screen.Loop(container); + if(exit_flag) + selected = -1; + qDebug() << "Selected element = " << selected; + return selected; +} + +//TorrentSession is created here. +int MainScreen::startDownloadIndex(std::vector res, int index) +{ + qDebug() << "Enter Download Path: "; + auto expected_path = runBasicShell(res[index]); + if(!expected_path.has_value()) { + return -1; + } + auto download_path = expected_path.value(); + TorrentSession *session = new TorrentSession(res[index], download_path); + auto pid = session->start(); + if(pid != 0) { + session->deleteLater(); + qDebug() << "Parent done."; + } + return pid; +} + +std::expected MainScreen::runBasicShell(SearchItem it) +{ + qDebug() << "Running Basic Shell"; + qDebug() << "Valid commands: ls, cd, select, pwd, rmdir, mkdir, set, lstags and exit."; + 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."; + qDebug() << "As a select argument the tag name be prefixed by '$' like '$TAG_NAME'."; + qDebug() << "set: used for setting a tag. set [TAG_NAME] [FOLDER PATH]"; + qDebug() << "lstags: Lists all tags stored."; + namespace fs = std::filesystem; + while(true) { + std::print("nsh|{}>",fs::current_path()); + std::string command; + std::getline(std::cin, command); + if(command == "ls") { + system("ls"); + } + if(command.starts_with("cd")) { + auto arg = stripForArgument(command, 1); + auto abs = fs::absolute(arg); + if(fs::is_directory(abs)) + fs::current_path(abs); + else { + if(arg.starts_with('$')) { + arg = arg.erase(0, 1); + auto dir = Tags::getTag(tags_file_path, arg); + if(fs::exists(fs::absolute(dir))) { + fs::current_path(fs::absolute(dir)); + continue; + } + else { + qDebug() << "No such tag: " << arg; + } + } + qDebug() << "No such directory: " << abs.string(); + } + } + if(command == "pwd") { + qDebug() << QString::fromStdString(fs::current_path()); + } + if(command == "exit") { + break; + } + if(command.starts_with("select")) { + if(command == "select") { + Tags::setTag(tags_file_path, "last", fs::current_path().string()); + return fs::current_path(); + } + auto arg = stripForArgument(command, 1); + if(arg.starts_with("$")) { + arg = arg.erase(0,1); + auto string_path = Tags::getTag(tags_file_path, arg); + if(string_path == "") { + qDebug() << arg << " tag not found in tags file."; + continue; + } + Tags::setTag(tags_file_path, "last", fs::current_path().string()); + return fs::path(string_path); + } + else if(fs::is_directory(fs::absolute(arg))) { + auto path = fs::absolute(arg); + Tags::setTag(tags_file_path, "last", path.string()); + return path; + } + else { + qDebug() << arg << " is not a valid path or tag."; + } + } + if(command.starts_with("set")) { + auto arg1 = stripForArgument(command, 1); + auto arg2 = stripForArgument(command, 2); + if(arg1 == "" || arg2 == "") { + qDebug() << "Invalid arguments for set."; + continue; + } + if(!fs::is_directory(fs::canonical(arg2))) { + qDebug() << arg2 << " is not a valid path."; + continue; + } + if(arg1.contains(' ') || arg1.contains('\n') || arg1.contains('\t') || arg1.contains('\r') || arg1.contains('\v')) { + qDebug() << arg1 << " can not have a whitespace char."; + continue; + } + arg2 = fs::canonical(fs::path(arg2)); + qDebug() << "Setting tag:" << arg1 << ":" << arg2; + Tags::setTag(tags_file_path, arg1, arg2); + } + if(command == "lstags") { + auto tags = Tags::getAllTags(tags_file_path); + for(auto &[key, value]: tags) { + qDebug() << key << ": " << value; + } + } + if(command.starts_with("rmdir")) { + auto arg = stripForArgument(command, 1); + fs::path path_to_remove = fs::current_path() / fs::path(arg); + std::string t; + if(!fs::is_directory(path_to_remove)) { + qDebug() << QString::fromStdString(path_to_remove) << " is not a directory."; + continue; + } + qDebug() << "Are you sure you want to remove: " << QString::fromStdString(path_to_remove) << "(y/n)?"; + std::cin >> t; + std::cin.clear(); + if(t.size() >= 1) { + if(t[0] == 'y' || t[0] == 'Y') + fs::remove(path_to_remove); + } + } + 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); + } + } + } + return std::unexpected(-1); +} + +std::string MainScreen::stripForArgument(std::string command, int index) +{ + std::istringstream iss(command); + std::string word; + std::vector tokens; + while (iss >> word) { + tokens.push_back(word); + } + if (index > 0 && index < tokens.size()) { + return tokens[index]; + } + return ""; +} + +void MainScreen::renderSearchPage_(QNetworkReply *rep) +{ + QByteArray data = rep->readAll(); + qDebug() << "Render Search Page"; + std::string page = data.toStdString(); + if(data.size() == 0) { + qDebug() << "Invalid reply; Retrying..."; + auto req = rep->request(); + manager->get(req); + rep->deleteLater(); + return; + } + rep->deleteLater(); + auto res = parseSearchPage(page); + for(auto i: res) { + i.printData(); + qDebug() << "================================"; + } + qDebug() << "Total got from response: " << res.size(); + int index = displaySearchMenu(res); + if(index >= 0) { + auto pid = startDownloadIndex(res, index); + if(pid == 0) + return; + } + renderHomePage(); +} + +void SearchItem::printData() +{ + qDebug() << "Title: " << title; + qDebug() << "Url: " << url; + qDebug() << "Size: " << size; + qDebug() << "Seeders: " << seeders; + qDebug() << "Leechers: " << leechers; + qDebug() << "Downloads: " << downloads; + qDebug() << "Timestamp: " << timestamp; + qDebug() << "Magnet: " << magnet; +} + +std::string MainScreen::Tags::getTag(std::string file_name, std::string key) +{ + QJsonDocument doc = QJsonDocument::fromJson(readFile(file_name)); + QJsonObject obj = doc.object(); + QString value = obj[QString::fromStdString(key)].toString(""); + return value.toStdString(); +} + +void MainScreen::Tags::setTag(std::string file_name, std::string key, std::string value) +{ + QJsonDocument doc = QJsonDocument::fromJson(readFile(file_name)); + QJsonObject obj = doc.object(); + obj[QString::fromStdString(key)] = QString::fromStdString(value); + doc.setObject(obj); + bool write = writeFile(file_name, doc.toJson()); + if(!write) + qDebug() << "Failed to write to file: " << file_name; +} + +QByteArray MainScreen::Tags::readFile(std::string file_name) +{ + + std::fstream fin(file_name, std::fstream::in); + if(!fin.is_open()) + return QByteArray(); + std::string data, fdata; + while(std::getline(fin, data)) { + fdata += data; + } + fin.flush(); + fin.close(); + return QByteArray::fromStdString(fdata); +} + +bool MainScreen::Tags::writeFile(std::string file_name, QByteArray data) +{ + std::fstream fout(file_name, std::fstream::out); + if(!fout.is_open()) + return false; + fout << data.toStdString(); + fout.flush(); + fout.close(); + return true; +} + +std::unordered_map MainScreen::Tags::getAllTags(std::string file_name) +{ + QJsonDocument doc = QJsonDocument::fromJson(readFile(file_name)); + QJsonObject obj = doc.object(); + std::unordered_map t; + for(auto &key: obj.keys()) { + t[key.toStdString()] = obj[key].toString().toStdString(); + } + return t; +} diff --git a/mainscreen.h b/mainscreen.h new file mode 100644 index 0000000..96a7d28 --- /dev/null +++ b/mainscreen.h @@ -0,0 +1,69 @@ +#ifndef MAINSCREEN_H +#define MAINSCREEN_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "torrentsession.h" + + + +struct SearchItem { + QString title; + QUrl url; + QString size; + QString seeders; + QString leechers; + QString downloads; + QString timestamp; + QString magnet; + void printData(); +}; + +class MainScreen : public QObject +{ + Q_OBJECT + + class Tags { + public: + Tags() = delete; + static std::string getTag(std::string file_name, std::string key); + static void setTag(std::string file_name, std::string key, std::string value); + static std::unordered_map getAllTags(std::string file_name); + + private: + static QByteArray readFile(std::string file_name); + static bool writeFile(std::string file_name, QByteArray data); + }; + + QNetworkAccessManager *manager = nullptr; + std::string tags_file_path = ""; +public: + explicit MainScreen(std::string search_term = "", QObject *parent = nullptr); +signals: +private: + void renderSearchPage(std::string search_term); + void renderHomePage(); + std::vector parseSearchPage(std::string html_page); + std::vector getStringEntries(std::vector res); + int displaySearchMenu(std::vector res); + int startDownloadIndex(std::vector res, int index); + std::expected runBasicShell(SearchItem it); + std::string stripForArgument(std::string command, int index); +private slots: + void renderSearchPage_(QNetworkReply *rep); +}; + +#endif // MAINSCREEN_H diff --git a/torrentsession.cpp b/torrentsession.cpp new file mode 100644 index 0000000..4f485ef --- /dev/null +++ b/torrentsession.cpp @@ -0,0 +1,152 @@ +#include "torrentsession.h" +#include "mainscreen.h" + +#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() +{ + +} + + +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; + } + } +} diff --git a/torrentsession.h b/torrentsession.h new file mode 100644 index 0000000..02a7ea7 --- /dev/null +++ b/torrentsession.h @@ -0,0 +1,69 @@ +#ifndef TORRENTSESSION_H +#define TORRENTSESSION_H + +#include +#include +#include +#include +#include +#include +#include +#include + + +struct SearchItem; + +class SocketManager: public QObject { + Q_OBJECT + QLocalServer *server = nullptr; + QLocalSocket *socket = nullptr; +public: + SocketManager(QObject *parent = nullptr); +signals: + void status(); + void resume(); + void pause(); + void remove(); + +public slots: + void start(); + void statusSignalFromTorrentSession(QByteArray data); + +private slots: + void addConnection(); + void commandFromClient(); +private: + void sendSignalToTorrentSession(QString command); + std::filesystem::path testDir(std::string dir_name, std::string file_format); + std::string numToStr(int num); +}; + +class TorrentSession : public QObject +{ + Q_OBJECT + SearchItem *item = nullptr; + std::filesystem::path download_path; + enum class Status { + Downloading, + Paused, + }; + SocketManager *manager = nullptr; + QThread *socket_manager_thread = nullptr; + QTimer start_timer; +public: + explicit TorrentSession(SearchItem it, std::filesystem::path download_path, QObject *parent = nullptr); + int start(); +signals: + void startSocketManager(); + void sendStatusSignalToSocketManager(QByteArray data); +public slots: + void sendStatus(); + void pause(); + void resume(); + void remove(); +private slots: + void launchSession(); +signals: +}; + +#endif // TORRENTSESSION_H