Skip to content

Commit

Permalink
Merge pull request #100 from vktr/more-core-functions
Browse files Browse the repository at this point in the history
Add rechecking
  • Loading branch information
vktr committed Dec 27, 2022
2 parents 67882ab + 4dd4d45 commit a8fc06a
Show file tree
Hide file tree
Showing 13 changed files with 207 additions and 21 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions html/src/components/TorrentsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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} />
Expand Down
47 changes: 28 additions & 19 deletions html/src/components/TorrentsListItem.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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;
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -104,7 +106,7 @@ function progressLabel(torrent: Torrent) {
if (isPaused(torrent.flags)) {
return "checking_files_queued";
}
return "checking_files";
break;
}
case 2: return <Icon as={isAutoManaged(torrent.flags) && isPaused(torrent.flags) ? MdSchedule : MdFileCopy} mt={"1px"} w={3} h={3} />;
case 3: {
Expand Down Expand Up @@ -314,33 +316,40 @@ export default function TorrentsListItem(props: TorrentsListItemProps) {
size={"sm"}
/>
<MenuList>
{
isPaused(props.torrent.flags)
? <MenuItem
icon={<MdPlayArrow />}
onClick={() => props.onResume(props.torrent)}
>
Resume
</MenuItem>
: <MenuItem
icon={<MdPause />}
onClick={() => props.onPause(props.torrent)}
>
Pause
</MenuItem>
}
<MenuDivider />
<MenuGroup title="Actions">
{
isPaused(props.torrent.flags)
? <MenuItem
icon={<MdPlayArrow />}
onClick={() => props.onResume(props.torrent)}
>
Resume
</MenuItem>
: <MenuItem
icon={<MdPause />}
onClick={() => props.onPause(props.torrent)}
>
Pause
</MenuItem>
}
<MenuItem
icon={<MdSearch />}
onClick={() => props.onRecheck(props.torrent)}
>
Check files
</MenuItem>
<MenuItem
icon={<MdFolder />}
onClick={() => props.onMove(props.torrent)}
>
Move
Move contents
</MenuItem>
<MenuItem
icon={<MdDelete />}
onClick={() => props.onRemove(props.torrent)}
>
Remove
Remove torrent
</MenuItem>
</MenuGroup>
<MenuDivider />
Expand Down
5 changes: 5 additions & 0 deletions html/src/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export default function Home() {

const torrentsMove = useInvoker<void>("torrents.move");
const torrentsPause = useInvoker<void>("torrents.pause");
const torrentsRecheck = useInvoker<void>("torrents.recheck");
const torrentsRemove = useInvoker<void>("torrents.remove");
const torrentsResume = useInvoker<void>("torrents.resume");

Expand Down Expand Up @@ -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({
Expand Down
1 change: 1 addition & 0 deletions src/json/all.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
19 changes: 19 additions & 0 deletions src/json/torrentsrecheck.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include <nlohmann/json.hpp>

#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 = {};
}
}
2 changes: 2 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)},
Expand Down
2 changes: 1 addition & 1 deletion src/methods/torrentsmove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ void TorrentsMove::Invoke(const TorrentsMoveReq &req, WriteCb<TorrentsMoveRes> 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())
{
Expand Down
30 changes: 30 additions & 0 deletions src/methods/torrentsrecheck.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "torrentsrecheck.hpp"

#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/torrent_status.hpp>

#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<TorrentsRecheckRes> 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{});
}
24 changes: 24 additions & 0 deletions src/methods/torrentsrecheck.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#include "method.hpp"
#include "torrentsrecheck_reqres.hpp"

namespace porla
{
class ISession;
}

namespace porla::Methods
{
class TorrentsRecheck : public Method<TorrentsRecheckReq, TorrentsRecheckRes>
{
public:
explicit TorrentsRecheck(ISession& session);

protected:
void Invoke(const TorrentsRecheckReq& req, WriteCb<TorrentsRecheckRes> cb) override;

private:
ISession& m_session;
};
}
18 changes: 18 additions & 0 deletions src/methods/torrentsrecheck_reqres.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <optional>
#include <string>

#include <libtorrent/info_hash.hpp>

namespace porla::Methods
{
struct TorrentsRecheckReq
{
libtorrent::info_hash_t info_hash;
};

struct TorrentsRecheckRes
{
};
}
73 changes: 72 additions & 1 deletion src/session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,60 @@ int Session::Query(const std::string_view& query, const std::function<int(sqlite
return SQLITE_OK;
}

void Session::Recheck(const lt::info_hash_t &hash)
{
const auto& handle = m_torrents.at(hash);

// If the torrent is paused, it must be resumed in order to be rechecked.
// It should also not be auto managed, so remove it from that as well.
// When the session posts a torrent_check alert, restore its flags.

bool was_auto_managed = false;
bool was_paused = false;
int alert_type = lt::torrent_checked_alert::alert_type;

if ((handle.flags() & lt::torrent_flags::auto_managed) == lt::torrent_flags::auto_managed)
{
handle.unset_flags(lt::torrent_flags::auto_managed);
was_auto_managed = true;
}

if ((handle.flags() & lt::torrent_flags::paused) == lt::torrent_flags::paused)
{
handle.resume();
was_paused = true;
}

if (!m_oneshot_torrent_callbacks.contains({ alert_type, hash }))
{
m_oneshot_torrent_callbacks.insert({{ alert_type, hash }, {}});
}

m_oneshot_torrent_callbacks.at({ alert_type, hash }).emplace_back(
[&, hash, was_auto_managed, was_paused]()
{
if (!m_torrents.contains(hash))
{
return;
}

// TODO: Unsure about the order here. If there are reports that force-checking a torrent
// leads to any issues with resume/pause, the order of these statements might matter.

if (was_auto_managed)
{
m_torrents.at(hash).set_flags(lt::torrent_flags::auto_managed);
}

if (was_paused)
{
m_torrents.at(hash).pause();
}
});

handle.force_recheck();
}

void Session::Remove(const lt::info_hash_t& hash, bool remove_data)
{
lt::torrent_handle th = m_torrents.at(hash);
Expand Down Expand Up @@ -506,16 +560,33 @@ void Session::ReadAlerts()

break;
}
case lt::torrent_checked_alert::alert_type:
{
const auto tca = lt::alert_cast<lt::torrent_checked_alert>(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<lt::torrent_finished_alert>(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);
}

Expand Down
3 changes: 3 additions & 0 deletions src/session.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int(sqlite3_stmt*)>& 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;
Expand Down Expand Up @@ -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<int(sqlite3_stmt*)>& 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;
Expand Down Expand Up @@ -149,5 +151,6 @@ namespace porla

std::unique_ptr<libtorrent::session> m_session;
std::map<libtorrent::info_hash_t, libtorrent::torrent_handle> m_torrents;
std::map<std::pair<int, libtorrent::info_hash_t>, std::vector<std::function<void()>>> m_oneshot_torrent_callbacks;
};
}

0 comments on commit a8fc06a

Please sign in to comment.