diff --git a/Tribler/Core/Libtorrent/LibtorrentMgr.py b/Tribler/Core/Libtorrent/LibtorrentMgr.py
index adc7e9c657d..1008c45004c 100644
--- a/Tribler/Core/Libtorrent/LibtorrentMgr.py
+++ b/Tribler/Core/Libtorrent/LibtorrentMgr.py
@@ -445,7 +445,9 @@ def get_metainfo(self, infohash_or_magnet, callback, timeout=30, timeout_callbac
infohash_bin = infohash_or_magnet if not magnet else parse_magnetlink(magnet)[1]
infohash = hexlify(infohash_bin)
- if infohash in self.torrents:
+ if infohash in self.torrents and hasattr(self.torrents[infohash], 'handle'):
+ metainfo = {"info": lt.bdecode(get_info_from_handle(self.torrents[infohash].handle).metadata())}
+ callback(metainfo)
return
with self.metainfo_lock:
@@ -536,7 +538,7 @@ def got_metainfo(self, infohash, timeout=False):
metainfo["nodes"] = []
if peers and notify:
self.notifier.notify(NTFY_TORRENTS, NTFY_MAGNET_GOT_PEERS, infohash_bin, len(peers))
- metainfo["initial peers"] = peers
+
metainfo["leechers"] = leechers
metainfo["seeders"] = seeders
diff --git a/Tribler/Core/Modules/MetadataStore/OrmBindings/channel_metadata.py b/Tribler/Core/Modules/MetadataStore/OrmBindings/channel_metadata.py
index 8790615d1c9..5b3740e0710 100644
--- a/Tribler/Core/Modules/MetadataStore/OrmBindings/channel_metadata.py
+++ b/Tribler/Core/Modules/MetadataStore/OrmBindings/channel_metadata.py
@@ -16,7 +16,7 @@
from Tribler.Core.Category.Category import default_category_filter
from Tribler.Core.Modules.MetadataStore.OrmBindings.channel_node import COMMITTED, LEGACY_ENTRY, NEW, PUBLIC_KEY_LEN, \
TODELETE, UPDATED
-from Tribler.Core.Modules.MetadataStore.serialization import CHANNEL_TORRENT, ChannelMetadataPayload
+from Tribler.Core.Modules.MetadataStore.serialization import CHANNEL_TORRENT, ChannelMetadataPayload, REGULAR_TORRENT
from Tribler.Core.TorrentDef import TorrentDef
from Tribler.Core.Utilities.tracker_utils import get_uniformed_tracker_url
from Tribler.Core.exceptions import DuplicateChannelIdError, DuplicateTorrentFileError
@@ -253,6 +253,18 @@ def get_torrent(self, infohash):
"""
return db.TorrentMetadata.get(public_key=self.public_key, infohash=infohash)
+ @db_session
+ def torrent_exists(self, infohash):
+ """
+ Return True if torrent with given infohash exists in the user channel
+ :param infohash: The infohash of the torrent
+ :return: True if torrent exists else False
+ """
+ return db.TorrentMetadata.exists(lambda g: g.metadata_type == REGULAR_TORRENT
+ and g.status != LEGACY_ENTRY
+ and g.public_key == self.public_key
+ and g.infohash == database_blob(infohash))
+
@db_session
def add_torrent_to_channel(self, tdef, extra_info=None):
"""
@@ -297,6 +309,10 @@ def add_torrent_to_channel(self, tdef, extra_info=None):
torrent_metadata = db.TorrentMetadata.from_dict(new_entry_dict)
return torrent_metadata
+ @db_session
+ def copy_to_channel(self, infohash):
+ return db.TorrentMetadata.copy_to_channel(infohash)
+
@property
def dirty(self):
return self.contents.where(lambda g: g.status in [NEW, TODELETE, UPDATED]).exists()
diff --git a/Tribler/Core/Modules/MetadataStore/OrmBindings/torrent_metadata.py b/Tribler/Core/Modules/MetadataStore/OrmBindings/torrent_metadata.py
index 1c6b8f3e342..0fa6bf79a1e 100644
--- a/Tribler/Core/Modules/MetadataStore/OrmBindings/torrent_metadata.py
+++ b/Tribler/Core/Modules/MetadataStore/OrmBindings/torrent_metadata.py
@@ -1,15 +1,16 @@
from __future__ import absolute_import
-from binascii import hexlify
+from binascii import hexlify, unhexlify
from datetime import datetime
from pony import orm
from pony.orm import db_session, desc, raw_sql, select
from Tribler.Core.Category.FamilyFilter import default_xxx_filter
-from Tribler.Core.Modules.MetadataStore.OrmBindings.channel_node import LEGACY_ENTRY, TODELETE, UPDATED
+from Tribler.Core.Modules.MetadataStore.OrmBindings.channel_node import LEGACY_ENTRY, NEW, TODELETE, UPDATED
from Tribler.Core.Modules.MetadataStore.serialization import REGULAR_TORRENT, TorrentMetadataPayload
from Tribler.Core.Utilities.tracker_utils import get_uniformed_tracker_url
+from Tribler.Core.Utilities.utilities import is_channel_public_key, is_hex_string, is_infohash
from Tribler.pyipv8.ipv8.database import database_blob
@@ -71,6 +72,15 @@ def search_keyword(cls, query, lim=100):
fts_ids = raw_sql(
'SELECT rowid FROM FtsIndex WHERE FtsIndex MATCH $query ORDER BY bm25(FtsIndex) LIMIT $lim')
+
+ # TODO: Check for complex query
+ normal_query = query.replace('"', '').replace("*", "")
+ if is_hex_string(normal_query) and len(normal_query) % 2 == 0:
+ query_blob = database_blob(unhexlify(normal_query))
+ if is_channel_public_key(normal_query):
+ return cls.select(lambda g: g.public_key == query_blob or g.rowid in fts_ids)
+ if is_infohash(normal_query):
+ return cls.select(lambda g: g.infohash == query_blob or g.rowid in fts_ids)
return cls.select(lambda g: g.rowid in fts_ids)
@classmethod
@@ -217,4 +227,30 @@ def update_properties(self, update_dict):
self.timestamp = self._clock.tick()
self.sign()
+ @classmethod
+ @db_session
+ def copy_to_channel(cls, infohash, public_key=None):
+ """
+ Create a new signed copy of the given torrent metadata
+ :param metadata: Metadata to copy
+ :return: New TorrentMetadata signed with your key
+ """
+
+ existing = cls.get(public_key=public_key, infohash=infohash) if public_key \
+ else cls.select(lambda g: g.infohash == database_blob(infohash)).first()
+
+ if not existing:
+ return None
+
+ new_entry_dict = {
+ "infohash": existing.infohash,
+ "title": existing.title,
+ "tags": existing.tags,
+ "size": existing.size,
+ "torrent_date": existing.torrent_date,
+ "tracker_info": existing.tracker_info,
+ "status": NEW
+ }
+ return db.TorrentMetadata.from_dict(new_entry_dict)
+
return TorrentMetadata
diff --git a/Tribler/Core/Modules/restapi/mychannel_endpoint.py b/Tribler/Core/Modules/restapi/mychannel_endpoint.py
index be885290469..010111b3b25 100644
--- a/Tribler/Core/Modules/restapi/mychannel_endpoint.py
+++ b/Tribler/Core/Modules/restapi/mychannel_endpoint.py
@@ -1,6 +1,8 @@
from __future__ import absolute_import
import base64
+import codecs
+import json
import logging
import os
from binascii import hexlify, unhexlify
@@ -18,7 +20,7 @@
import Tribler.Core.Utilities.json_util as json
from Tribler.Core.Modules.restapi.metadata_endpoint import SpecificChannelTorrentsEndpoint
from Tribler.Core.TorrentDef import TorrentDef
-from Tribler.Core.Utilities.utilities import http_get
+from Tribler.Core.Utilities.utilities import http_get, is_infohash, parse_magnetlink
from Tribler.Core.exceptions import DuplicateTorrentFileError
from Tribler.pyipv8.ipv8.database import database_blob
@@ -265,6 +267,10 @@ def _on_timeout(_):
deferred = http_get(uri)
deferred.addCallback(_on_url_fetched)
elif uri.startswith("magnet:"):
+ _, xt, _ = parse_magnetlink(uri)
+ if xt and is_infohash(codecs.encode(xt, 'hex')) \
+ and (my_channel.torrent_exists(xt) or my_channel.copy_to_channel(xt)):
+ return json.dumps({"added": 1})
try:
self.session.lm.ltmgr.get_metainfo(uri, callback=deferred.callback,
timeout=30, timeout_callback=_on_timeout, notify=True)
diff --git a/Tribler/Core/Utilities/utilities.py b/Tribler/Core/Utilities/utilities.py
index 4bbcc1fa4bc..212a5816a01 100644
--- a/Tribler/Core/Utilities/utilities.py
+++ b/Tribler/Core/Utilities/utilities.py
@@ -225,3 +225,19 @@ def has_bep33_support():
Also see https://github.com/devos50/libtorrent/tree/bep33_support
"""
return 'dht_pkt_alert' in dir(libtorrent)
+
+
+def is_infohash(infohash):
+ return infohash and len(infohash) == 40 and is_hex_string(infohash)
+
+
+def is_channel_public_key(key):
+ return key and len(key) == 128 and is_hex_string(key)
+
+
+def is_hex_string(text):
+ try:
+ int(text, 16)
+ return True
+ except ValueError:
+ return False
diff --git a/Tribler/Test/Core/Libtorrent/test_libtorrent_mgr.py b/Tribler/Test/Core/Libtorrent/test_libtorrent_mgr.py
index 4fa878263e2..cd60f0d4c44 100644
--- a/Tribler/Test/Core/Libtorrent/test_libtorrent_mgr.py
+++ b/Tribler/Test/Core/Libtorrent/test_libtorrent_mgr.py
@@ -100,7 +100,7 @@ def test_get_metainfo(self):
def metainfo_cb(metainfo):
self.assertEqual(metainfo, {'info': {'pieces': ['a']}, 'leechers': 0,
- 'nodes': [], 'seeders': 0, 'initial peers': []})
+ 'nodes': [], 'seeders': 0})
test_deferred.callback(None)
infohash = "a" * 20
@@ -152,7 +152,7 @@ def test_got_metainfo(self):
def metainfo_cb(metainfo):
self.assertDictEqual(metainfo, {'info': {'pieces': ['a']}, 'leechers': 0,
- 'nodes': [], 'seeders': 0, 'initial peers': []})
+ 'nodes': [], 'seeders': 0})
test_deferred.callback(None)
fake_handle = MockObject()
diff --git a/Tribler/Test/Core/Modules/MetadataStore/test_channel_metadata.py b/Tribler/Test/Core/Modules/MetadataStore/test_channel_metadata.py
index 31310be9737..da8e2569808 100644
--- a/Tribler/Test/Core/Modules/MetadataStore/test_channel_metadata.py
+++ b/Tribler/Test/Core/Modules/MetadataStore/test_channel_metadata.py
@@ -201,6 +201,40 @@ def test_add_torrent_to_channel(self):
self.assertTrue(channel_metadata.contents_list)
self.assertRaises(DuplicateTorrentFileError, channel_metadata.add_torrent_to_channel, tdef, None)
+ @db_session
+ def test_torrent_exists_in_channel(self):
+ """
+ Test torrent already exists in the channel.
+ """
+ channel_metadata = self.mds.ChannelMetadata.create_channel('test', 'test')
+ self.mds.TorrentMetadata.from_dict(dict(self.torrent_template, infohash="1"))
+ self.assertTrue(channel_metadata.torrent_exists("1"))
+ self.assertFalse(channel_metadata.torrent_exists("0"))
+
+ @db_session
+ def test_copy_to_channel(self):
+ """
+ Test copying a torrent from an another channel.
+ """
+ self.mds.ChannelNode._my_key = default_eccrypto.generate_key('low')
+ channel1 = self.mds.ChannelMetadata(infohash=str(random.getrandbits(160)))
+ self.mds.TorrentMetadata.from_dict(dict(self.torrent_template, infohash="1"))
+
+ self.mds.ChannelNode._my_key = default_eccrypto.generate_key('low')
+ channel2 = self.mds.ChannelMetadata(infohash=str(random.getrandbits(160)))
+
+ # Trying copying existing torrent to channel
+ new_torrent = channel2.copy_to_channel("1")
+ self.assertIsNotNone(new_torrent)
+ self.assertEqual(1, len(channel1.contents_list))
+ self.assertEqual(1, len(channel2.contents_list))
+
+ # Try copying non-existing torrent ot channel
+ new_torrent2 = channel2.copy_to_channel("2")
+ self.assertIsNone(new_torrent2)
+ self.assertEqual(1, len(channel1.contents_list))
+ self.assertEqual(1, len(channel2.contents_list))
+
@db_session
def test_restore_torrent_in_channel(self):
"""
diff --git a/Tribler/Test/Core/Modules/MetadataStore/test_torrent_metadata.py b/Tribler/Test/Core/Modules/MetadataStore/test_torrent_metadata.py
index 55095941503..f603b0c0554 100644
--- a/Tribler/Test/Core/Modules/MetadataStore/test_torrent_metadata.py
+++ b/Tribler/Test/Core/Modules/MetadataStore/test_torrent_metadata.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
+import codecs
import random
from datetime import datetime
@@ -135,6 +136,34 @@ def test_stemming_search(self):
results = self.mds.TorrentMetadata.search_keyword("sheeps")[:]
self.assertEqual(torrent.rowid, results[0].rowid)
+ @db_session
+ def test_infohash_search(self):
+ """
+ Test searching for torrents with infohash works.
+ """
+ infohash = b"e84213a794f3ccd890382a54a64ca68b7e925433"
+ torrent = self.mds.TorrentMetadata.from_dict(dict(rnd_torrent(), infohash=codecs.decode(infohash, 'hex'),
+ title="mountains sheep", tags="video"))
+
+ # Search with the hex encoded infohash
+ query = '"%s"*' % infohash
+ results = self.mds.TorrentMetadata.search_keyword(query)[:]
+ self.assertEqual(torrent.rowid, results[0].rowid)
+
+ @db_session
+ def test_channel_public_key_search(self):
+ """
+ Test searching for channels with public key works.
+ """
+ self.mds.ChannelNode._my_key = default_eccrypto.generate_key('curve25519')
+ channel = self.mds.ChannelMetadata(title='My channel', infohash=str(random.getrandbits(160)))
+
+ # Search with the hex encoded channel public key
+ query = '"%s"*' % codecs.encode(channel.public_key, 'hex')
+ results = self.mds.TorrentMetadata.search_keyword(query)[:]
+ self.assertIsNotNone(results)
+ self.assertTrue(channel.rowid, results[0].rowid)
+
@db_session
def test_get_autocomplete_terms(self):
"""
diff --git a/Tribler/Test/Core/Utilities/test_utilities.py b/Tribler/Test/Core/Utilities/test_utilities.py
index 76f430a9059..a66f0f918f8 100644
--- a/Tribler/Test/Core/Utilities/test_utilities.py
+++ b/Tribler/Test/Core/Utilities/test_utilities.py
@@ -6,7 +6,8 @@
from twisted.web.util import Redirect
from Tribler.Core.Utilities.network_utils import get_random_port
-from Tribler.Core.Utilities.utilities import http_get, is_simple_match_query, is_valid_url, parse_magnetlink
+from Tribler.Core.Utilities.utilities import http_get, is_channel_public_key, is_infohash, is_simple_match_query, \
+ is_valid_url, parse_magnetlink
from Tribler.Test.test_as_server import AbstractServer
from Tribler.Test.tools import trial_timeout
@@ -82,3 +83,24 @@ def test_simple_search_query(self):
query2 = '"\xc1ubuntu"* OR "debian"*'
self.assertFalse(is_simple_match_query(query2))
+
+ def test_is_infohash(self):
+ hex_40 = "DC4B96CF85A85CEEDB8ADC4B96CF85A85CEEDB8A"
+ self.assertTrue(is_infohash(hex_40))
+
+ hex_not_40 = "DC4B96CF85A85CEEDB8ADC4B96CF85"
+ self.assertFalse(is_infohash(hex_not_40))
+
+ not_hex = "APPLE6CF85A85CEEDB8ADC4B96CF85A85CEEDB8A"
+ self.assertFalse(is_infohash(not_hex))
+
+ def test_is_channel_public_key(self):
+ hex_128 = "224b20c30b90d0fc7b2cf844f3d651de4481e21c7cdbbff258fa737d117d2c4ac7536de5cc93f4e9d5" \
+ "1012a1ae0c46e9a05505bd017f0ecb78d8eec4506e848a"
+ self.assertTrue(is_channel_public_key(hex_128))
+
+ hex_not_128 = "DC4B96CF85A85CEEDB8ADC4B96CF85"
+ self.assertFalse(is_channel_public_key(hex_not_128))
+
+ not_hex = "APPLE6CF85A85CEEDB8ADC4B96CF85A85CEEDB8A"
+ self.assertFalse(is_channel_public_key(not_hex))
diff --git a/TriblerGUI/qt_resources/mainwindow.ui b/TriblerGUI/qt_resources/mainwindow.ui
index 3d819e6f773..9b0911e6788 100644
--- a/TriblerGUI/qt_resources/mainwindow.ui
+++ b/TriblerGUI/qt_resources/mainwindow.ui
@@ -3736,19 +3736,25 @@ font-weight: bold;
- 8
+ 12
20
-
-
+
+
+
+
color: #eee; font-size: 15px;
- Preview
+ Preview ⟳
+
+
+ 8
@@ -4421,7 +4427,7 @@ border-top: 1px solid #555;
0
0
- 755
+ 300
646
@@ -4505,7 +4511,7 @@ color: white;
-
- Family filter enabled?
+ Family filter enabled?
(requires Tribler restart)
@@ -7897,15 +7903,15 @@ margin-right: 10px;
PointingHandCursor
+
+ Click to copy magnet link
+
border: none;
-
- Click to copy magnet link
-
../images/magnet.png../images/magnet.png
diff --git a/TriblerGUI/qt_resources/torrent_details_container.ui b/TriblerGUI/qt_resources/torrent_details_container.ui
index b18b58c362c..9b44fc71af5 100644
--- a/TriblerGUI/qt_resources/torrent_details_container.ui
+++ b/TriblerGUI/qt_resources/torrent_details_container.ui
@@ -270,6 +270,9 @@ QTabBar::tab:selected {
00000000000000000000
+
+ Qt::TextSelectableByMouse
+
-
diff --git a/TriblerGUI/widgets/channelpage.py b/TriblerGUI/widgets/channelpage.py
index be591112235..c75017237de 100644
--- a/TriblerGUI/widgets/channelpage.py
+++ b/TriblerGUI/widgets/channelpage.py
@@ -34,6 +34,13 @@ def initialize_channel_page(self, gui_settings):
commit_control = self.window().channel_page_container.content_table.delegate.commit_control
self.window().channel_page_container.content_table.delegate.controls.remove(commit_control)
+ # To reload the preview
+ self.window().channel_preview_label.clicked.connect(self.preview_clicked)
+
+ def preview_clicked(self):
+ self.controller.fetch_preview()
+ self.initialize_with_channel(self.channel_info)
+
def initialize_with_channel(self, channel_info):
self.channel_info = channel_info
diff --git a/TriblerGUI/widgets/lazytableview.py b/TriblerGUI/widgets/lazytableview.py
index 09cd8f286ab..ed071be93f5 100644
--- a/TriblerGUI/widgets/lazytableview.py
+++ b/TriblerGUI/widgets/lazytableview.py
@@ -94,6 +94,10 @@ def on_subscribe_control_clicked(self, index):
data={"subscribe": int(not status)}, method='POST')
index.model().data_items[index.row()][u'subscribed'] = int(not status)
+ # Update votes
+ votes = index.model().data_items[index.row()][u'votes']
+ index.model().data_items[index.row()][u'votes'] = votes + 1 if not status else votes - 1
+
class ItemClickedMixin(TriblerContentTableView):
def on_table_item_clicked(self, item):
@@ -148,8 +152,22 @@ def on_delete_button_clicked(self, index):
data={"status" : COMMIT_STATUS_TODELETE}, method='PATCH')
+class AddToChannelButtonMixin(CommitControlMixin):
+
+ def on_add_to_channel_button_clicked(self, _):
+ for row in self.selectionModel().selectedRows():
+ post_data = {"uri": index2uri(row)}
+ request_mgr = TriblerRequestManager()
+ request_mgr.perform_request("mychannel/torrents", self.on_torrent_added,
+ method='PUT', data=post_data)
+
+ def on_torrent_added(self, _):
+ self.window().edit_channel_page.load_my_torrents()
+ self.window().tray_show_message("Channel update", "Torrent is added to your channel")
+
+
class SearchResultsTableView(ItemClickedMixin, DownloadButtonMixin, PlayButtonMixin, SubscribeButtonMixin,
- TriblerContentTableView):
+ AddToChannelButtonMixin, TriblerContentTableView):
on_subscribed_channel = pyqtSignal(QModelIndex)
on_unsubscribed_channel = pyqtSignal(QModelIndex)
@@ -178,7 +196,7 @@ def resizeEvent(self, _):
class TorrentsTableView(ItemClickedMixin, DeleteButtonMixin, DownloadButtonMixin, PlayButtonMixin,
- TriblerContentTableView):
+ AddToChannelButtonMixin, TriblerContentTableView):
"""
This table displays various torrents.
"""
diff --git a/TriblerGUI/widgets/tablecontentdelegate.py b/TriblerGUI/widgets/tablecontentdelegate.py
index 89ccbe95cf6..986dcb0c4d4 100644
--- a/TriblerGUI/widgets/tablecontentdelegate.py
+++ b/TriblerGUI/widgets/tablecontentdelegate.py
@@ -193,7 +193,7 @@ def paint_exact(self, painter, option, index):
data_item = index.model().data_items[index.row()]
if index == self.hover_index:
- self.subscribe_control.paint_hover(painter, option.rect, index)
+ self.subscribe_control.paint_hover(painter, option.rect, index, toggled=data_item['subscribed'])
else:
self.subscribe_control.paint(painter, option.rect, index, toggled=data_item['subscribed'])
@@ -330,8 +330,8 @@ def paint(self, painter, rect, _, toggled=False):
icon.paint(painter, icon_rect)
- def paint_hover(self, painter, rect, _):
- icon = self.hover_icon
+ def paint_hover(self, painter, rect, _index, toggled=False):
+ icon = self.on_icon if toggled else self.hover_icon
x = rect.left() + (rect.width() - self.w) / 2
y = rect.top() + (rect.height() - self.h) / 2
icon_rect = QRect(x, y, self.w, self.h)
diff --git a/TriblerGUI/widgets/triblertablecontrollers.py b/TriblerGUI/widgets/triblertablecontrollers.py
index 9d53b4d3506..7290d46acdf 100644
--- a/TriblerGUI/widgets/triblertablecontrollers.py
+++ b/TriblerGUI/widgets/triblertablecontrollers.py
@@ -6,8 +6,13 @@
import uuid
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QCursor
+from PyQt5.QtWidgets import QAction
+
from six import text_type
+from TriblerGUI.tribler_action_menu import TriblerActionMenu
from TriblerGUI.tribler_request_manager import TriblerRequestManager
@@ -74,7 +79,7 @@ def perform_query(self, **kwargs):
sort_by, sort_asc = self._get_sort_parameters()
kwargs.update({
"uuid": self.query_uuid,
- "filter": to_fts_query(self.query_text),
+ "filter": to_fts_query(kwargs.pop('query_filter') if 'query_filter' in kwargs else self.query_text),
"sort_by": sort_by,
"sort_asc": sort_asc,
"hide_xxx": self.model.hide_xxx})
@@ -110,7 +115,7 @@ def is_new_result(self, response):
:param response: List of items
:return: True for fresh response else False
"""
- if 'uuid' in response and response['uuid'] != self.query_uuid:
+ if self.query_uuid and 'uuid' in response and response['uuid'] != self.query_uuid:
return False
if 'first' in response and response['first'] < self.model.rowCount():
return False
@@ -155,7 +160,45 @@ def _on_selection_changed(self, _):
window.resize(window.geometry().width() - 1, window.geometry().height())
-class SearchResultsTableViewController(TableSelectionMixin, TriblerTableViewController):
+class ContextMenuMixin(object):
+
+ table_view = None
+
+ def enable_context_menu(self, widget):
+ self.table_view = widget
+ self.table_view.setContextMenuPolicy(Qt.CustomContextMenu)
+ self.table_view.customContextMenuRequested.connect(self._show_context_menu)
+
+ def _show_context_menu(self, pos):
+ if not self.table_view:
+ return
+
+ item_index = self.table_view.indexAt(pos)
+ if not item_index or item_index.row() < 0:
+ return
+
+ menu = TriblerActionMenu(self.table_view)
+
+ # Single selection menu items
+ num_selected = len(self.table_view.selectionModel().selectedRows())
+ if num_selected == 1:
+ self.add_menu_item(menu, ' Download ', item_index, self.table_view.on_download_button_clicked)
+ self.add_menu_item(menu, ' Play ', item_index, self.table_view.on_play_button_clicked)
+
+ if not isinstance(self, MyTorrentsTableViewController):
+ self.add_menu_item(menu, ' Add to channel ', item_index, self.table_view.on_add_to_channel_button_clicked)
+ else:
+ self.add_menu_item(menu, ' Remove from channel ', item_index, self.table_view.on_delete_button_clicked)
+
+ menu.exec_(QCursor.pos())
+
+ def add_menu_item(self, menu, name, item_index, callback):
+ action = QAction(name, self.table_view)
+ action.triggered.connect(lambda _: callback(item_index))
+ menu.addAction(action)
+
+
+class SearchResultsTableViewController(TableSelectionMixin, ContextMenuMixin, TriblerTableViewController):
"""
Controller for the table view that handles search results.
"""
@@ -165,6 +208,7 @@ def __init__(self, model, table_view, details_container, num_results_label=None)
self.num_results_label = num_results_label
self.details_container = details_container
table_view.selectionModel().selectionChanged.connect(self._on_selection_changed)
+ self.enable_context_menu(self.table_view)
def perform_query(self, **kwargs):
"""
@@ -199,7 +243,7 @@ def perform_query(self, **kwargs):
super(ChannelsTableViewController, self).perform_query(**kwargs)
-class TorrentsTableViewController(TableSelectionMixin, FilterInputMixin, TriblerTableViewController):
+class TorrentsTableViewController(TableSelectionMixin, FilterInputMixin, ContextMenuMixin, TriblerTableViewController):
"""
This class manages a list with torrents.
"""
@@ -212,6 +256,7 @@ def __init__(self, model, table_view, details_container, num_results_label=None,
table_view.selectionModel().selectionChanged.connect(self._on_selection_changed)
if self.filter_input:
self.filter_input.textChanged.connect(self._on_filter_input_change)
+ self.enable_context_menu(self.table_view)
def perform_query(self, **kwargs):
if "rest_endpoint_url" not in kwargs:
@@ -219,6 +264,15 @@ def perform_query(self, **kwargs):
"rest_endpoint_url": "metadata/channels/%s/torrents" % self.model.channel_pk})
super(TorrentsTableViewController, self).perform_query(**kwargs)
+ def fetch_preview(self):
+ params = {'query_filter': self.model.channel_pk,
+ 'metadata_type': 'torrent',
+ 'rest_endpoint_url': 'search',
+ 'first': 1,
+ 'last': 50
+ }
+ super(TorrentsTableViewController, self).perform_query(**params)
+
class MyTorrentsTableViewController(TorrentsTableViewController):
"""