diff --git a/CMakeLists.txt b/CMakeLists.txt index db30a328..9f840322 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,6 +154,8 @@ add_executable( src/methods/torrentspropertiesget.hpp src/methods/torrentsquery.cpp src/methods/torrentsquery.hpp + src/methods/torrentsrecheck.cpp + src/methods/torrentsrecheck.hpp src/methods/torrentsremove.cpp src/methods/torrentsremove.hpp src/methods/torrentsresume.cpp diff --git a/html/src/components/TorrentsList.tsx b/html/src/components/TorrentsList.tsx index fb4a0b02..97dbaaf9 100644 --- a/html/src/components/TorrentsList.tsx +++ b/html/src/components/TorrentsList.tsx @@ -6,6 +6,7 @@ type TorrentsListProps = { isDeleting: (torrent: Torrent) => boolean; onMove: (torrent: Torrent) => void; onPause: (torrent: Torrent) => void; + onRecheck: (torrent: Torrent) => void; onRemove: (torrent: Torrent) => void; onResume: (torrent: Torrent) => void; onShowProperties: (torrent: Torrent) => void; @@ -64,6 +65,7 @@ export default function TorrentsList(props: TorrentsListProps) { onMove={props.onMove} isDeleting={props.isDeleting} onPause={props.onPause} + onRecheck={props.onRecheck} onRemove={props.onRemove} onResume={props.onResume} onShowProperties={props.onShowProperties} /> diff --git a/html/src/components/TorrentsListItem.tsx b/html/src/components/TorrentsListItem.tsx index 0e4231aa..1c61efed 100644 --- a/html/src/components/TorrentsListItem.tsx +++ b/html/src/components/TorrentsListItem.tsx @@ -1,7 +1,7 @@ import { Box, CircularProgress, CircularProgressLabel, Flex, Grid, GridItem, HStack, Icon, IconButton, Link, Menu, MenuButton, MenuDivider, MenuGroup, MenuItem, MenuList, Text, textDecoration, useColorMode } from "@chakra-ui/react"; import { filesize } from "filesize"; import { IconType } from "react-icons/lib"; -import { MdCheck, MdDelete, MdDriveFileMove, MdFileCopy, MdFolder, MdFolderOpen, MdLabel, MdOutlineMoreVert, MdOutlineReport, MdPause, MdPlayArrow, MdSchedule, MdTag, MdUpload, MdViewList } from "react-icons/md"; +import { MdCheck, MdDelete, MdDriveFileMove, MdFileCopy, MdFolder, MdFolderOpen, MdLabel, MdOutlineMoreVert, MdOutlineReport, MdPause, MdPlayArrow, MdSchedule, MdSearch, MdTag, MdUpload, MdViewList } from "react-icons/md"; import { Torrent } from "../types" import useNinja from "../contexts/ninja"; @@ -12,6 +12,7 @@ type TorrentsListItemProps = { isDeleting: (torrent: Torrent) => boolean; onMove: (torrent: Torrent) => void; onPause: (torrent: Torrent) => void; + onRecheck: (torrent: Torrent) => void; onRemove: (torrent: Torrent) => void; onResume: (torrent: Torrent) => void; onShowProperties: (torrent: Torrent) => void; @@ -62,6 +63,7 @@ function stateColor(torrent: Torrent) { } switch (torrent.state) { + case 1: return "gray.500"; case 2: return "gray.600"; case 3: return "blue.500"; case 5: return "green.300"; @@ -104,7 +106,7 @@ function progressLabel(torrent: Torrent) { if (isPaused(torrent.flags)) { return "checking_files_queued"; } - return "checking_files"; + break; } case 2: return ; case 3: { @@ -314,33 +316,40 @@ export default function TorrentsListItem(props: TorrentsListItemProps) { size={"sm"} /> + { + isPaused(props.torrent.flags) + ? } + onClick={() => props.onResume(props.torrent)} + > + Resume + + : } + onClick={() => props.onPause(props.torrent)} + > + Pause + + } + - { - isPaused(props.torrent.flags) - ? } - onClick={() => props.onResume(props.torrent)} - > - Resume - - : } - onClick={() => props.onPause(props.torrent)} - > - Pause - - } + } + onClick={() => props.onRecheck(props.torrent)} + > + Check files + } onClick={() => props.onMove(props.torrent)} > - Move + Move contents } onClick={() => props.onRemove(props.torrent)} > - Remove + Remove torrent diff --git a/html/src/pages/Home.tsx b/html/src/pages/Home.tsx index 114bd562..74180ef7 100644 --- a/html/src/pages/Home.tsx +++ b/html/src/pages/Home.tsx @@ -38,6 +38,7 @@ export default function Home() { const torrentsMove = useInvoker("torrents.move"); const torrentsPause = useInvoker("torrents.pause"); + const torrentsRecheck = useInvoker("torrents.recheck"); const torrentsRemove = useInvoker("torrents.remove"); const torrentsResume = useInvoker("torrents.resume"); @@ -176,6 +177,10 @@ export default function Home() { }); await mutate(); }} + onRecheck={async (t) => { + await torrentsRecheck({ info_hash: t.info_hash }); + await mutate(); + }} onRemove={(torrent) => setRemoveTorrent(torrent)} onResume={async (t) => { await torrentsResume({ diff --git a/src/json/all.hpp b/src/json/all.hpp index e7405a00..144e972c 100644 --- a/src/json/all.hpp +++ b/src/json/all.hpp @@ -20,6 +20,7 @@ #include "torrentspeerslist.hpp" #include "torrentspropertiesget.hpp" #include "torrentsquery.hpp" +#include "torrentsrecheck.hpp" #include "torrentsremove.hpp" #include "torrentsresume.hpp" #include "torrentspropertiesset.hpp" diff --git a/src/json/torrentsrecheck.hpp b/src/json/torrentsrecheck.hpp new file mode 100644 index 00000000..9c4940ac --- /dev/null +++ b/src/json/torrentsrecheck.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include "../methods/torrentsrecheck_reqres.hpp" +#include "ltinfohash.hpp" +#include "utils.hpp" + +namespace porla::Methods +{ + NLOHMANN_JSONIFY_ALL_THINGS( + TorrentsRecheckReq, + info_hash) + + static void to_json(json& j, const TorrentsRecheckRes& res) + { + j = {}; + } +} diff --git a/src/main.cpp b/src/main.cpp index e75bb9fe..774a60cf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,6 +43,7 @@ #include "methods/torrentspeersadd.hpp" #include "methods/torrentspeerslist.hpp" #include "methods/torrentsquery.hpp" +#include "methods/torrentsrecheck.hpp" #include "methods/torrentsremove.hpp" #include "methods/torrentsresume.hpp" #include "methods/torrentspropertiesget.hpp" @@ -151,6 +152,7 @@ int main(int argc, char* argv[]) {"torrents.peers.list", porla::Methods::TorrentsPeersList(session)}, {"torrents.properties.get", porla::Methods::TorrentsPropertiesGet(session)}, {"torrents.properties.set", porla::Methods::TorrentsPropertiesSet(session)}, + {"torrents.recheck", porla::Methods::TorrentsRecheck(session)}, {"torrents.query", porla::Methods::TorrentsQuery(session)}, {"torrents.remove", porla::Methods::TorrentsRemove(session)}, {"torrents.resume", porla::Methods::TorrentsResume(session)}, diff --git a/src/methods/torrentsmove.cpp b/src/methods/torrentsmove.cpp index ce159cc0..6fcd1b3b 100644 --- a/src/methods/torrentsmove.cpp +++ b/src/methods/torrentsmove.cpp @@ -21,7 +21,7 @@ void TorrentsMove::Invoke(const TorrentsMoveReq &req, WriteCb c return cb.Error(-1, "Torrent not found"); } - lt::move_flags_t flags = lt::move_flags_t::always_replace_files; + lt::move_flags_t flags = lt::move_flags_t::dont_replace; if (req.flags.has_value()) { diff --git a/src/methods/torrentsrecheck.cpp b/src/methods/torrentsrecheck.cpp new file mode 100644 index 00000000..ac87dd87 --- /dev/null +++ b/src/methods/torrentsrecheck.cpp @@ -0,0 +1,30 @@ +#include "torrentsrecheck.hpp" + +#include +#include + +#include "../session.hpp" + +using porla::Methods::TorrentsRecheck; +using porla::Methods::TorrentsRecheckReq; +using porla::Methods::TorrentsRecheckRes; + +TorrentsRecheck::TorrentsRecheck(porla::ISession &session) + : m_session(session) +{ +} + +void TorrentsRecheck::Invoke(const TorrentsRecheckReq &req, WriteCb cb) +{ + auto const& torrents = m_session.Torrents(); + auto const& handle = torrents.find(req.info_hash); + + if (handle == torrents.end()) + { + return cb.Error(-1, "Torrent not found"); + } + + m_session.Recheck(req.info_hash); + + return cb.Ok(TorrentsRecheckRes{}); +} diff --git a/src/methods/torrentsrecheck.hpp b/src/methods/torrentsrecheck.hpp new file mode 100644 index 00000000..f2fd31d5 --- /dev/null +++ b/src/methods/torrentsrecheck.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "method.hpp" +#include "torrentsrecheck_reqres.hpp" + +namespace porla +{ + class ISession; +} + +namespace porla::Methods +{ + class TorrentsRecheck : public Method + { + public: + explicit TorrentsRecheck(ISession& session); + + protected: + void Invoke(const TorrentsRecheckReq& req, WriteCb cb) override; + + private: + ISession& m_session; + }; +} diff --git a/src/methods/torrentsrecheck_reqres.hpp b/src/methods/torrentsrecheck_reqres.hpp new file mode 100644 index 00000000..c60a7243 --- /dev/null +++ b/src/methods/torrentsrecheck_reqres.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include + +namespace porla::Methods +{ + struct TorrentsRecheckReq + { + libtorrent::info_hash_t info_hash; + }; + + struct TorrentsRecheckRes + { + }; +} diff --git a/src/session.cpp b/src/session.cpp index 644a6b67..d2e16144 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -397,6 +397,60 @@ int Session::Query(const std::string_view& query, const std::function(alert); + BOOST_LOG_TRIVIAL(info) << "Torrent " << tca->torrent_name() << " finished checking"; + + if (m_oneshot_torrent_callbacks.contains({ alert->type(), tca->handle.info_hashes()})) + { + for (auto && cb : m_oneshot_torrent_callbacks.at({ alert->type(), tca->handle.info_hashes() })) + { + cb(); + } + + m_oneshot_torrent_callbacks.erase({ alert->type(), tca->handle.info_hashes() }); + } + + break; + } case lt::torrent_finished_alert::alert_type: { auto tfa = lt::alert_cast(alert); auto const& status = tfa->handle.status(); - BOOST_LOG_TRIVIAL(info) << "Torrent " << status.name << " finished"; if (status.total_download > 0) { // Only emit this event if we have downloaded any data this session. + BOOST_LOG_TRIVIAL(info) << "Torrent " << status.name << " finished"; m_torrentFinished(status); } diff --git a/src/session.hpp b/src/session.hpp index f3d0b965..356d2e84 100644 --- a/src/session.hpp +++ b/src/session.hpp @@ -49,6 +49,7 @@ namespace porla virtual void ApplySettings(const libtorrent::settings_pack& settings) = 0; virtual void Pause() = 0; virtual int Query(const std::string_view& query, const std::function& cb) = 0; + virtual void Recheck(const lt::info_hash_t& hash) = 0; virtual void Remove(const lt::info_hash_t& hash, bool remove_data) = 0; virtual void Resume() = 0; virtual libtorrent::settings_pack Settings() = 0; @@ -118,6 +119,7 @@ namespace porla void ApplySettings(const libtorrent::settings_pack& settings) override; void Pause() override; int Query(const std::string_view& query, const std::function& cb) override; + void Recheck(const lt::info_hash_t& hash) override; void Remove(const lt::info_hash_t& hash, bool remove_data) override; void Resume() override; libtorrent::settings_pack Settings() override; @@ -149,5 +151,6 @@ namespace porla std::unique_ptr m_session; std::map m_torrents; + std::map, std::vector>> m_oneshot_torrent_callbacks; }; }