Progress: 50%.
This commit is contained in:
parent
159d38e210
commit
fa9ca77bfd
4 changed files with 227 additions and 16 deletions
179
mainscreen.cpp
179
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)
|
void MainScreen::renderSearchPage(std::string search_term)
|
||||||
{
|
{
|
||||||
if(!manager) {
|
if(!manager) {
|
||||||
|
|
@ -53,6 +65,15 @@ void MainScreen::renderSearchPage(std::string search_term)
|
||||||
void MainScreen::renderHomePage()
|
void MainScreen::renderHomePage()
|
||||||
{
|
{
|
||||||
qDebug() << "Render Home Page";
|
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<SearchItem> MainScreen::parseSearchPage(std::string html_page)
|
std::vector<SearchItem> MainScreen::parseSearchPage(std::string html_page)
|
||||||
|
|
@ -198,6 +219,7 @@ int MainScreen::startDownloadIndex(std::vector<SearchItem> res, int index)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
auto download_path = expected_path.value();
|
auto download_path = expected_path.value();
|
||||||
|
res[index].download_path = download_path;
|
||||||
TorrentSession *session = new TorrentSession(res[index], download_path);
|
TorrentSession *session = new TorrentSession(res[index], download_path);
|
||||||
auto pid = session->start();
|
auto pid = session->start();
|
||||||
if(pid != 0) {
|
if(pid != 0) {
|
||||||
|
|
@ -211,6 +233,7 @@ std::expected<std::string,int> MainScreen::runBasicShell(SearchItem it)
|
||||||
{
|
{
|
||||||
qDebug() << "Running Basic Shell";
|
qDebug() << "Running Basic Shell";
|
||||||
qDebug() << "Valid commands: ls, cd, select, pwd, rmdir, mkdir, set, lstags and exit.";
|
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"
|
qDebug() << "select: Selects the present working dir"
|
||||||
" as download directory if no arguments are provided."
|
" as download directory if no arguments are provided."
|
||||||
"Else uses the provided argument as a tag or a path.";
|
"Else uses the provided argument as a tag or a path.";
|
||||||
|
|
@ -218,8 +241,10 @@ std::expected<std::string,int> MainScreen::runBasicShell(SearchItem it)
|
||||||
qDebug() << "set: used for setting a tag. set [TAG_NAME] [FOLDER PATH]";
|
qDebug() << "set: used for setting a tag. set [TAG_NAME] [FOLDER PATH]";
|
||||||
qDebug() << "lstags: Lists all tags stored.";
|
qDebug() << "lstags: Lists all tags stored.";
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
std::string extras = "";
|
||||||
while(true) {
|
while(true) {
|
||||||
std::print("nsh|{}>",fs::current_path());
|
std::print("nsh|{}>{}",fs::current_path(), extras);
|
||||||
|
extras = "";
|
||||||
std::string command;
|
std::string command;
|
||||||
std::getline(std::cin, command);
|
std::getline(std::cin, command);
|
||||||
if(command == "ls") {
|
if(command == "ls") {
|
||||||
|
|
@ -319,12 +344,18 @@ std::expected<std::string,int> MainScreen::runBasicShell(SearchItem it)
|
||||||
}
|
}
|
||||||
if(command.starts_with("mkdir")) {
|
if(command.starts_with("mkdir")) {
|
||||||
auto arg = stripForArgument(command, 1);
|
auto arg = stripForArgument(command, 1);
|
||||||
|
if(!arg.starts_with('$')) {
|
||||||
fs::path path_to_create = fs::current_path() / fs::path(arg);
|
fs::path path_to_create = fs::current_path() / fs::path(arg);
|
||||||
bool t = fs::create_directory(path_to_create);
|
bool t = fs::create_directory(path_to_create);
|
||||||
if(!t) {
|
if(!t) {
|
||||||
qDebug() << "Failed to create path: " << QString::fromStdString(path_to_create);
|
qDebug() << "Failed to create path: " << QString::fromStdString(path_to_create);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(arg == "$title") {
|
||||||
|
extras = "mkdir " + it.title.toStdString();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return std::unexpected(-1);
|
return std::unexpected(-1);
|
||||||
}
|
}
|
||||||
|
|
@ -343,6 +374,11 @@ std::string MainScreen::stripForArgument(std::string command, int index)
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainScreen::statusFromSocket(Status st)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void MainScreen::renderSearchPage_(QNetworkReply *rep)
|
void MainScreen::renderSearchPage_(QNetworkReply *rep)
|
||||||
{
|
{
|
||||||
QByteArray data = rep->readAll();
|
QByteArray data = rep->readAll();
|
||||||
|
|
@ -381,6 +417,7 @@ void SearchItem::printData()
|
||||||
qDebug() << "Downloads: " << downloads;
|
qDebug() << "Downloads: " << downloads;
|
||||||
qDebug() << "Timestamp: " << timestamp;
|
qDebug() << "Timestamp: " << timestamp;
|
||||||
qDebug() << "Magnet: " << magnet;
|
qDebug() << "Magnet: " << magnet;
|
||||||
|
qDebug() << "Download path: " << download_path.string();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchItem::writeData(std::string file_path)
|
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;
|
qDebug() << "Failed to write to file: " << file_path;
|
||||||
return;
|
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 << title.toStdString() << std::endl;
|
||||||
out << url.toDisplayString().toStdString() << std::endl;
|
out << url.toDisplayString().toStdString() << std::endl;
|
||||||
out << magnet.toStdString() << std::endl;
|
out << magnet.toStdString() << std::endl;
|
||||||
|
out << download_path.string() << std::endl;
|
||||||
out.flush();
|
out.flush();
|
||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
|
|
@ -404,14 +446,16 @@ void SearchItem::readFromData(std::string file_path)
|
||||||
qDebug() << "Failed to read from file: " << file_path;
|
qDebug() << "Failed to read from file: " << file_path;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::string stitle, surl, smagnet;
|
std::string stitle, surl, smagnet, spath;
|
||||||
in >> stitle;
|
std::getline(in, stitle);
|
||||||
in >> surl;
|
std::getline(in, surl);
|
||||||
in >> smagnet;
|
std::getline(in, smagnet);
|
||||||
|
std::getline(in, spath);
|
||||||
in.close();
|
in.close();
|
||||||
title = QString::fromStdString(stitle);
|
title = QString::fromStdString(stitle);
|
||||||
url = QUrl(QString::fromStdString(surl));
|
url = QUrl(QString::fromStdString(surl));
|
||||||
magnet = QString::fromStdString(smagnet);
|
magnet = QString::fromStdString(smagnet);
|
||||||
|
download_path = std::filesystem::canonical(spath);
|
||||||
in.close();
|
in.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -470,3 +514,128 @@ std::unordered_map<std::string, std::string> MainScreen::Tags::getAllTags(std::s
|
||||||
}
|
}
|
||||||
return t;
|
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 = "<STATUS>";
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
32
mainscreen.h
32
mainscreen.h
|
|
@ -29,11 +29,37 @@ struct SearchItem {
|
||||||
QString downloads;
|
QString downloads;
|
||||||
QString timestamp;
|
QString timestamp;
|
||||||
QString magnet;
|
QString magnet;
|
||||||
|
std::filesystem::path download_path;
|
||||||
void printData();
|
void printData();
|
||||||
void writeData(std::string file_path);
|
void writeData(std::string file_path);
|
||||||
void readFromData(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<std::string, QLocalSocket *> 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
|
class MainScreen : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
@ -52,9 +78,15 @@ class MainScreen : public QObject
|
||||||
|
|
||||||
QNetworkAccessManager *manager = nullptr;
|
QNetworkAccessManager *manager = nullptr;
|
||||||
std::string tags_file_path = "";
|
std::string tags_file_path = "";
|
||||||
|
Socket *socket = nullptr;
|
||||||
|
QThread *socket_thread = nullptr;
|
||||||
public:
|
public:
|
||||||
explicit MainScreen(std::string search_term = "", QObject *parent = nullptr);
|
explicit MainScreen(std::string search_term = "", QObject *parent = nullptr);
|
||||||
signals:
|
signals:
|
||||||
|
void startSocket();
|
||||||
|
public slots:
|
||||||
|
void resumeOldTorrentSession(SearchItem it);
|
||||||
|
void statusFromSocket(Status st);
|
||||||
private:
|
private:
|
||||||
void renderSearchPage(std::string search_term);
|
void renderSearchPage(std::string search_term);
|
||||||
void renderHomePage();
|
void renderHomePage();
|
||||||
|
|
|
||||||
|
|
@ -94,9 +94,13 @@ void TorrentSession::launchSession()
|
||||||
if(!std::filesystem::exists(resume_files_location)) {
|
if(!std::filesystem::exists(resume_files_location)) {
|
||||||
std::filesystem::create_directories(resume_files_location);
|
std::filesystem::create_directories(resume_files_location);
|
||||||
}
|
}
|
||||||
|
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) /
|
item->writeData((fs::path(resume_files_location) /
|
||||||
fs::path(item->title.toStdString())).string() + ".searchitem");
|
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
|
// 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) /
|
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());
|
lt::add_torrent_params magnet = lt::parse_magnet_uri(item->magnet.toStdString());
|
||||||
if (buf.size()) {
|
if (buf.size()) {
|
||||||
|
qDebug() << "Loading info from resume file.";
|
||||||
lt::add_torrent_params atp = lt::read_resume_data(buf);
|
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
|
magnet.save_path = download_path.string(); // save location
|
||||||
|
|
||||||
ses.async_add_torrent(std::move(magnet));
|
ses.async_add_torrent(std::move(magnet));
|
||||||
|
|
@ -144,7 +153,6 @@ void TorrentSession::launchSession()
|
||||||
h.save_resume_data(lt::torrent_handle::only_if_modified
|
h.save_resume_data(lt::torrent_handle::only_if_modified
|
||||||
| lt::torrent_handle::save_info_dict);
|
| lt::torrent_handle::save_info_dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
// when resume data is ready, save it
|
// when resume data is ready, save it
|
||||||
if (auto rd = lt::alert_cast<lt::save_resume_data_alert>(a)) {
|
if (auto rd = lt::alert_cast<lt::save_resume_data_alert>(a)) {
|
||||||
std::ofstream of(resume_files_name, std::ios_base::binary);
|
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));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
ses.post_torrent_updates();
|
ses.post_torrent_updates();
|
||||||
QCoreApplication::processEvents();
|
QCoreApplication::processEvents();
|
||||||
// save resume data once every 30 seconds
|
// save resume data once every 3 seconds
|
||||||
if (clk::now() - last_save_resume > std::chrono::seconds(30)) {
|
if (clk::now() - last_save_resume > std::chrono::seconds(3)) {
|
||||||
h.save_resume_data(lt::torrent_handle::only_if_modified
|
h.save_resume_data(lt::torrent_handle::only_if_modified
|
||||||
| lt::torrent_handle::save_info_dict);
|
| lt::torrent_handle::save_info_dict);
|
||||||
last_save_resume = clk::now();
|
last_save_resume = clk::now();
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,8 @@ class TorrentSession : public QObject
|
||||||
enum class Status {
|
enum class Status {
|
||||||
Downloading,
|
Downloading,
|
||||||
Paused,
|
Paused,
|
||||||
|
Finished,
|
||||||
|
Seeding,
|
||||||
};
|
};
|
||||||
SocketManager *manager = nullptr;
|
SocketManager *manager = nullptr;
|
||||||
QThread *socket_manager_thread = nullptr;
|
QThread *socket_manager_thread = nullptr;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue