Skip to content

Commit

Permalink
[UI] Add and improve trackers tab
Browse files Browse the repository at this point in the history
First, added trackers tab to the WebUI.
Second, now we can view all the trackers and view each:
* status
* number of peers
* additional message
Third, information about DHT, PeX and LSD is also added.

closes: https://dev.deluge-torrent.org/ticket/1015
  • Loading branch information
DjLegolas committed Feb 26, 2022
1 parent 7510710 commit 86f65bd
Show file tree
Hide file tree
Showing 8 changed files with 474 additions and 26 deletions.
27 changes: 27 additions & 0 deletions deluge/ui/gtk3/glade/main_window.tabs.menu_trackers.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.0"/>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">list-add-symbolic</property>
<property name="icon_size">1</property>
</object>
<object class="GtkMenu" id="menu_trackers_tab">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImageMenuItem" id="edit_trackers_menuitem">
<property name="label" translatable="yes">_Edit Trackers</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Edit all trackers</property>
<property name="use_underline">True</property>
<property name="image">image1</property>
<property name="use_stock">False</property>
<signal name="activate" handler="on_menuitem_edit_trackers_activate" swapped="no"/>
</object>
</child>
</object>
</interface>
30 changes: 30 additions & 0 deletions deluge/ui/gtk3/glade/main_window.tabs.ui
Original file line number Diff line number Diff line change
Expand Up @@ -1661,6 +1661,36 @@
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="trackers2_tab">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<object class="GtkTreeView" id="trackers_listview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeselection3"/>
</child>
</object>
</child>
</object>
<packing>
<property name="position">6</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="trackers2_tab_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Trackers</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="position">7</property>
<property name="tab_fill">False</property>
</packing>
</child>
</object>
</child>
</object>
Expand Down
1 change: 1 addition & 0 deletions deluge/ui/gtk3/mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ def patched_connect_signals(*a, **k):
'main_window.tabs.ui',
'main_window.tabs.menu_file.ui',
'main_window.tabs.menu_peer.ui',
'main_window.tabs.menu_trackers.ui',
]
for filename in ui_filenames:
self.main_builder.add_from_file(
Expand Down
196 changes: 170 additions & 26 deletions deluge/ui/gtk3/trackers_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,59 +8,203 @@

import logging

from gi.repository.Gtk import CellRendererText, ListStore, SortType, TreeViewColumn

import deluge.component as component
from deluge.common import ftime

from .tab_data_funcs import fcount, ftranslate, fyes_no
from .torrentdetails import Tab

log = logging.getLogger(__name__)


class TrackersTab(Tab):
class Trackers2Tab(Tab):
def __init__(self):
super().__init__('Trackers', 'trackers_tab', 'trackers_tab_label')

self.add_tab_widget('summary_next_announce', ftime, ('next_announce',))
self.add_tab_widget('summary_tracker', None, ('tracker_host',))
self.add_tab_widget('summary_tracker_status', ftranslate, ('tracker_status',))
self.add_tab_widget('summary_tracker_total', fcount, ('trackers',))
self.add_tab_widget('summary_private', fyes_no, ('private',))
super().__init__('Trackers2', 'trackers2_tab', 'trackers2_tab_label')

self.trackers_menu = self.main_builder.get_object('menu_trackers_tab')
component.get('MainWindow').connect_signals(self)

self.listview = self.main_builder.get_object('trackers_listview')
self.listview.props.has_tooltip = True
self.listview.connect('button-press-event', self._on_button_press_event)

# url, status, peers, message
self.liststore = ListStore(str, str, int, str)

# key is url, item is row iter
self.trackers = {}
self.constant_rows = {}

# self.treeview.append_column(
# Gtk.TreeViewColumn(_('Tier'), Gtk.CellRendererText(), text=0)
# )
column = TreeViewColumn(_('Tracker'))
render = CellRendererText()
column.pack_start(render, False)
column.add_attribute(render, 'text', 0)
column.set_clickable(True)
column.set_resizable(True)
column.set_expand(False)
column.set_min_width(150)
column.set_reorderable(True)
self.listview.append_column(column)

column = TreeViewColumn(_('Status'))
render = CellRendererText()
column.pack_start(render, False)
column.add_attribute(render, 'text', 1)
column.set_clickable(True)
column.set_resizable(True)
column.set_expand(False)
column.set_min_width(50)
column.set_reorderable(True)
self.listview.append_column(column)

column = TreeViewColumn(_('Peers'))
render = CellRendererText()
column.pack_start(render, False)
column.add_attribute(render, 'text', 2)
column.set_clickable(True)
column.set_resizable(True)
column.set_expand(False)
column.set_min_width(50)
column.set_reorderable(True)
self.listview.append_column(column)

column = TreeViewColumn(_('Message'))
render = CellRendererText()
column.pack_start(render, False)
column.add_attribute(render, 'text', 3)
column.set_clickable(True)
column.set_resizable(True)
column.set_expand(False)
column.set_min_width(100)
column.set_reorderable(True)
self.listview.append_column(column)

self.listview.set_model(self.liststore)
self.liststore.set_sort_column_id(0, SortType.ASCENDING)

self.torrent_id = None

self._fill_constant_rows()

def _fill_constant_rows(self):
for item in ['DHT', 'PeX', 'LSD']:
row = self.liststore.append(
[
f'*** {item} ***',
'',
0,
'',
]
)

self.constant_rows[item.lower()] = row

def update(self):
# Get the first selected torrent
selected = component.get('TorrentView').get_selected_torrents()
torrent_id = component.get('TorrentView').get_selected_torrents()

# Only use the first torrent in the list or return if None selected
if selected:
selected = selected[0]
if torrent_id:
torrent_id = torrent_id[0]
else:
self.clear()
self.liststore.clear()
self._fill_constant_rows()
return

if torrent_id != self.torrent_id:
# We only want to do this if the torrent_id has changed
self.liststore.clear()
self.trackers = {}
self._fill_constant_rows()
self.torrent_id = torrent_id

session = component.get('SessionProxy')
session.get_torrent_status(selected, self.status_keys).addCallback(
self._on_get_torrent_status

tracker_keys = [
'trackers',
'trackers_status',
'trackers_peers',
]

session.get_torrent_status(torrent_id, tracker_keys).addCallback(
self._on_get_torrent_tracker_status
)
session.get_torrent_status(torrent_id, ['peers_source']).addCallback(
self._on_get_peers_source_status
)

def _on_get_torrent_tracker_status(self, status):
# Check to see if we got valid data from the core
if not status:
return

def _on_get_torrent_status(self, status):
new_trackers = set()
for tracker in status['trackers']:
new_trackers.add(tracker['url'])
tracker_url = tracker['url']
tracker_status = status['trackers_status'].get(tracker_url, '')
tracker_peers = status['trackers_peers'].get(tracker_url, 0)
tracker_message = tracker.get('message', '')
if tracker['url'] in self.trackers:
row = self.trackers[tracker['url']]
if not self.liststore.iter_is_valid(row):
# This iter is invalid, delete it and continue to next iteration
del self.trackers[tracker['url']]
continue
values = self.liststore.get(row, 1, 2, 3)
if tracker_status != values[0]:
self.liststore.set_value(row, 1, tracker_status)
if tracker_peers != values[1]:
self.liststore.set_value(row, 2, tracker_peers)
if tracker_message != values[2]:
self.liststore.set_value(row, 3, tracker_message)
else:
row = self.liststore.append(
[
tracker_url,
tracker_status,
tracker_peers,
tracker_message,
]
)

self.trackers[tracker['url']] = row

# Now we need to remove any tracker that were not in status['trackers'] list
for tracker in set(self.trackers).difference(new_trackers):
self.liststore.remove(self.trackers[tracker])
del self.trackers[tracker]

def _on_get_peers_source_status(self, status):
# Check to see if we got valid data from the core
if not status:
return

# Update all the tab label widgets
for widget in self.tab_widgets.values():
txt = self.widget_status_as_fstr(widget, status)
if widget.obj.get_text() != txt:
widget.obj.set_text(txt)
for const_values in status['peers_source']:
row = self.constant_rows[const_values['name']]
old_peers_value = self.liststore.get(row, 2)[0]
status = 'Working' if const_values['enabled'] else 'Disabled'
peers_count = const_values['count']
self.liststore.set_value(row, 1, status)
if peers_count != old_peers_value:
self.liststore.set_value(row, 2, peers_count)

def clear(self):
for widget in self.tab_widgets.values():
widget.obj.set_text('')

def on_button_edit_trackers_clicked(self, button):
self.liststore.clear()
self._fill_constant_rows()

def _on_button_press_event(self, widget, event):
"""This is a callback for showing the right-click context menu."""
log.debug('on_button_press_event')
# We only care about right-clicks
if event.button == 3:
self.trackers_menu.popup(None, None, None, None, event.button, event.time)
return True

def on_menuitem_edit_trackers_activate(self, button):
torrent_id = component.get('TorrentView').get_selected_torrent()
if torrent_id:
from .edittrackersdialog import EditTrackersDialog
Expand Down
6 changes: 6 additions & 0 deletions deluge/ui/web/js/deluge-all/Keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ Deluge.Keys = {
*/
Peers: ['peers'],

/**
* Keys used in the trackers tab of the statistics panel.
* <pre>['trackers']</pre>
*/
Trackers: ['trackers', 'trackers_status', 'trackers_peers'],

/**
* Keys used in the details tab of the statistics panel.
*/
Expand Down
40 changes: 40 additions & 0 deletions deluge/ui/web/js/deluge-all/data/TrackerRecord.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Deluge.data.TrackerRecord.js
*
* Copyright (c) Damien Churchill 2009-2010 <[email protected]>
*
* This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
* the additional special exception to link portions of this program with the OpenSSL library.
* See LICENSE for more details.
*/
Ext.namespace('Deluge.data');

/**
* Deluge.data.Tracker record
*
* @author Damien Churchill <[email protected]>
* @version 1.3
*
* @class Deluge.data.Tracker
* @extends Ext.data.Record
* @constructor
* @param {Object} data The tracker data
*/
Deluge.data.Tracker = Ext.data.Record.create([
{
name: 'tracker',
type: 'string',
},
{
name: 'status',
type: 'string',
},
{
name: 'peers',
type: 'int',
},
{
name: 'message',
type: 'string',
},
]);
1 change: 1 addition & 0 deletions deluge/ui/web/js/deluge-all/details/DetailsPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Deluge.details.DetailsPanel = Ext.extend(Ext.TabPanel, {
this.add(new Deluge.details.StatusTab());
this.add(new Deluge.details.DetailsTab());
this.add(new Deluge.details.FilesTab());
this.add(new Deluge.details.TrackersTab());
this.add(new Deluge.details.PeersTab());
this.add(new Deluge.details.OptionsTab());
},
Expand Down
Loading

0 comments on commit 86f65bd

Please sign in to comment.