premiere-libtorrent/examples/torrent_view.cpp

385 lines
10 KiB
C++

#include "torrent_view.hpp"
#include "print.hpp"
#include "libtorrent/torrent_status.hpp"
const int header_size = 2;
std::string torrent_state(lt::torrent_status const& s)
{
static char const* state_str[] =
{"checking (q)", "checking", "dl metadata"
, "downloading", "finished", "seeding", "allocating", "checking (r)"};
if (s.errc) return s.errc.message();
std::string ret;
if (s.paused && !s.auto_managed) ret += "paused";
else if (s.paused && s.auto_managed) ret += "queued";
else if (s.upload_mode) ret += "upload mode";
else ret += state_str[s.state];
if (!s.paused && !s.auto_managed) ret += " [F]";
char buf[10];
std::snprintf(buf, sizeof(buf), " (%.1f%%)", s.progress_ppm / 10000.f);
ret += buf;
return ret;
}
bool compare_torrent(lt::torrent_status const* lhs, lt::torrent_status const* rhs)
{
if (lhs->queue_position != -1 && rhs->queue_position != -1)
{
// both are downloading, sort by queue pos
return lhs->queue_position < rhs->queue_position;
}
else if (lhs->queue_position == -1 && rhs->queue_position == -1)
{
// both are seeding, sort by seed-rank
if (lhs->seed_rank != rhs->seed_rank)
return lhs->seed_rank > rhs->seed_rank;
return lhs->info_hash < rhs->info_hash;
}
return (lhs->queue_position == -1) < (rhs->queue_position == -1);
}
torrent_view::torrent_view()
: m_active_torrent(0)
, m_scroll_position(0)
, m_torrent_filter(0)
, m_width(80)
, m_height(30)
{}
void torrent_view::set_size(int width, int height)
{
if (m_width == width && m_height == height) return;
m_width = width;
m_height = height;
render();
}
int torrent_view::filter() const
{
return m_torrent_filter;
}
void torrent_view::set_filter(int filter)
{
if (filter == m_torrent_filter) return;
m_torrent_filter = filter;
update_filtered_torrents();
render();
}
// returns the lt::torrent_status of the currently selected torrent.
lt::torrent_status const& torrent_view::get_active_torrent() const
{
if (m_active_torrent >= int(m_filtered_handles.size()))
m_active_torrent = int(m_filtered_handles.size()) - 1;
if (m_active_torrent < 0) m_active_torrent = 0;
TORRENT_ASSERT(m_active_torrent >= 0);
return *m_filtered_handles[m_active_torrent];
}
lt::torrent_handle torrent_view::get_active_handle() const
{
if (m_active_torrent >= int(m_filtered_handles.size()))
m_active_torrent = int(m_filtered_handles.size()) - 1;
if (m_active_torrent < 0) m_active_torrent = 0;
TORRENT_ASSERT(m_active_torrent >= 0);
if (m_filtered_handles.empty()) return lt::torrent_handle();
return m_filtered_handles[m_active_torrent]->handle;
}
void torrent_view::update_torrents(std::vector<lt::torrent_status> const& st)
{
std::set<lt::torrent_handle> updates;
bool need_filter_update = false;
for (std::vector<lt::torrent_status>::const_iterator i = st.begin();
i != st.end(); ++i)
{
boost::unordered_set<lt::torrent_status>::iterator j = m_all_handles.find(*i);
// add new entries here
if (j == m_all_handles.end())
{
j = m_all_handles.insert(*i).first;
if (show_torrent(*j))
{
m_filtered_handles.push_back(&*j);
need_filter_update = true;
}
}
else
{
bool prev_show = show_torrent(*j);
((lt::torrent_status&)*j) = *i;
if (prev_show != show_torrent(*j))
need_filter_update = true;
else
updates.insert(i->handle);
}
}
if (need_filter_update)
{
update_filtered_torrents();
render();
}
else
{
int torrent_index = 0;
for (std::vector<lt::torrent_status const*>::iterator i
= m_filtered_handles.begin();
i != m_filtered_handles.end(); ++torrent_index)
{
if (torrent_index < m_scroll_position
|| torrent_index >= m_scroll_position + m_height - header_size)
{
++i;
continue;
}
lt::torrent_status const& s = **i;
++i;
if (!s.handle.is_valid())
continue;
if (updates.count(s.handle) == 0)
continue;
set_cursor_pos(0, header_size + torrent_index - m_scroll_position);
print_torrent(s, torrent_index == m_active_torrent);
}
}
}
int torrent_view::height() const
{
return m_height;
}
void torrent_view::arrow_up()
{
if (m_filtered_handles.empty()) return;
if (m_active_torrent <= 0) return;
if (m_active_torrent - 1 < m_scroll_position)
{
--m_active_torrent;
m_scroll_position = m_active_torrent;
TORRENT_ASSERT(m_scroll_position >= 0);
render();
return;
}
set_cursor_pos(0, header_size + m_active_torrent - m_scroll_position);
print_torrent(*m_filtered_handles[m_active_torrent], false);
--m_active_torrent;
TORRENT_ASSERT(m_active_torrent >= 0);
set_cursor_pos(0, header_size + m_active_torrent - m_scroll_position);
print_torrent(*m_filtered_handles[m_active_torrent], true);
}
void torrent_view::arrow_down()
{
if (m_filtered_handles.empty()) return;
if (m_active_torrent >= int(m_filtered_handles.size()) - 1) return;
int bottom_pos = m_height - header_size - 1;
if (m_active_torrent - m_scroll_position + 1 > bottom_pos)
{
++m_active_torrent;
m_scroll_position = m_active_torrent - bottom_pos;
TORRENT_ASSERT(m_scroll_position >= 0);
render();
return;
}
set_cursor_pos(0, header_size + m_active_torrent - m_scroll_position);
print_torrent(*m_filtered_handles[m_active_torrent], false);
TORRENT_ASSERT(m_active_torrent >= 0);
++m_active_torrent;
set_cursor_pos(0, header_size + m_active_torrent - m_scroll_position);
print_torrent(*m_filtered_handles[m_active_torrent], true);
}
void torrent_view::render()
{
print_tabs();
print_headers();
int lines_printed = header_size;
int torrent_index = 0;
for (std::vector<lt::torrent_status const*>::iterator i = m_filtered_handles.begin();
i != m_filtered_handles.end(); ++torrent_index)
{
if (torrent_index < m_scroll_position)
{
++i;
continue;
}
if (lines_printed >= m_height)
break;
lt::torrent_status const& s = **i;
if (!s.handle.is_valid())
{
i = m_filtered_handles.erase(i);
continue;
}
++i;
set_cursor_pos(0, torrent_index + header_size - m_scroll_position);
print_torrent(s, torrent_index == m_active_torrent);
++lines_printed;
}
clear_rows(torrent_index + header_size, m_height);
}
void torrent_view::print_tabs()
{
set_cursor_pos(0, 0);
char str[400];
int pos = 0;
char const* filter_names[] = { "all", "downloading", "non-paused"
, "seeding", "queued", "stopped", "checking", "loaded"};
for (int i = 0; i < int(sizeof(filter_names)/sizeof(filter_names[0])); ++i)
{
pos += std::snprintf(str+ pos, sizeof(str) - pos, "%s[%s]%s"
, m_torrent_filter == i?esc("7"):""
, filter_names[i], m_torrent_filter == i?esc("0"):"");
}
pos += std::snprintf(str + pos, sizeof(str) - pos, "\x1b[K");
if (m_width + 1 < int(sizeof(str)))
str[m_width + 1] = '\0';
print(str);
}
void torrent_view::print_headers()
{
set_cursor_pos(0, 1);
char str[400];
// print title bar for torrent list
std::snprintf(str, sizeof(str)
, " %-3s %-50s %-35s %-17s %-17s %-11s %-6s %-6s %-4s\x1b[K"
, "#", "Name", "Progress", "Download", "Upload", "Peers (D:S)"
, "Down", "Up", "Flags");
if (m_width + 1 < int(sizeof(str)))
str[m_width + 1] = '\0';
print(str);
}
void torrent_view::print_torrent(lt::torrent_status const& s, bool selected)
{
int pos = 0;
char str[512];
// the active torrent is highligted in the list
// this inverses the forground and background colors
char const* selection = "";
if (selected)
selection = "\x1b[1m\x1b[44m";
char queue_pos[16] = {0};
if (s.queue_position == -1)
std::snprintf(queue_pos, sizeof(queue_pos), "-");
else
std::snprintf(queue_pos, sizeof(queue_pos), "%d", s.queue_position);
std::string name = s.name;
if (name.size() > 50) name.resize(50);
color_code progress_bar_color = col_yellow;
if (s.errc) progress_bar_color = col_red;
else if (s.paused) progress_bar_color = col_blue;
else if (s.state == lt::torrent_status::downloading_metadata)
progress_bar_color = col_magenta;
else if (s.current_tracker.empty())
progress_bar_color = col_green;
pos += std::snprintf(str + pos, sizeof(str) - pos, "%s%c%-3s %-50s %s%s %s (%s) "
"%s (%s) %5d:%-5d %s %s %c"
, selection
, s.is_loaded ? 'L' : ' '
, queue_pos
, name.c_str()
, progress_bar(s.progress_ppm / 1000, 35, progress_bar_color, '-', '#', torrent_state(s)).c_str()
, selection
, color(add_suffix(s.download_rate, "/s"), col_green).c_str()
, color(add_suffix(s.total_download), col_green).c_str()
, color(add_suffix(s.upload_rate, "/s"), col_red).c_str()
, color(add_suffix(s.total_upload), col_red).c_str()
, s.num_peers - s.num_seeds, s.num_seeds
, color(add_suffix(s.all_time_download), col_green).c_str()
, color(add_suffix(s.all_time_upload), col_red).c_str()
, s.need_save_resume?'S':' ');
// if this is the selected torrent, restore the background color
if (selected)
pos += std::snprintf(str + pos, sizeof(str) - pos, "%s", esc("0"));
pos += std::snprintf(str + pos, sizeof(str) - pos, "\x1b[K");
print(str);
}
bool torrent_view::show_torrent(lt::torrent_status const& st)
{
switch (m_torrent_filter)
{
case torrents_all: return true;
case torrents_downloading:
return !st.paused
&& st.state != lt::torrent_status::seeding
&& st.state != lt::torrent_status::finished;
case torrents_not_paused: return !st.paused;
case torrents_seeding:
return !st.paused
&& (st.state == lt::torrent_status::seeding
|| st.state == lt::torrent_status::finished);
case torrents_queued: return st.paused && st.auto_managed;
case torrents_stopped: return st.paused && !st.auto_managed;
case torrents_checking: return st.state == lt::torrent_status::checking_files;
case torrents_loaded: return st.is_loaded;
}
return true;
}
// refresh all pointers in m_filtered_handles. This must be done when
// inserting or removing elements from m_all_handles, since pointers may
// be invalidated or when a torrent changes status to either become
// visible or filtered
void torrent_view::update_filtered_torrents()
{
m_scroll_position = 0;
m_filtered_handles.clear();
for (boost::unordered_set<lt::torrent_status>::iterator i = m_all_handles.begin()
, end(m_all_handles.end()); i != end; ++i)
{
if (!show_torrent(*i)) continue;
m_filtered_handles.push_back(&*i);
}
if (m_active_torrent >= int(m_filtered_handles.size())) m_active_torrent = int(m_filtered_handles.size()) - 1;
if (m_active_torrent < 0) m_active_torrent = 0;
TORRENT_ASSERT(m_active_torrent >= 0);
std::sort(m_filtered_handles.begin(), m_filtered_handles.end(), &compare_torrent);
}