Skip to content

Commit

Permalink
Merge pull request #2210 from devos50/torrents_in_channel_endpoint
Browse files Browse the repository at this point in the history
Implemented endpoint to get torrents in a specific discovered channel
  • Loading branch information
whirm committed May 17, 2016
2 parents d7cc3d8 + 7379600 commit cdd22cd
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 29 deletions.
3 changes: 3 additions & 0 deletions Tribler/Core/Modules/restapi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
"""
This package contains code for the Tribler HTTP API.
"""

VOTE_UNSUBSCRIBE = 0
VOTE_SUBSCRIBE = 2
96 changes: 73 additions & 23 deletions Tribler/Core/Modules/restapi/channels_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@

from twisted.web import http, resource

from Tribler.Core.Modules.restapi.util import convert_db_channel_to_json
from Tribler.Core.Modules.restapi import VOTE_SUBSCRIBE, VOTE_UNSUBSCRIBE
from Tribler.Core.Modules.restapi.util import convert_db_channel_to_json, convert_db_torrent_to_json
from Tribler.Core.simpledefs import NTFY_CHANNELCAST
from Tribler.community.allchannel.community import AllChannelCommunity

VOTE_UNSUBSCRIBE = 0
VOTE_SUBSCRIBE = 2


class BaseChannelsEndpoint(resource.Resource):
"""
Expand Down Expand Up @@ -49,11 +47,6 @@ def vote_for_channel(self, cid, vote):
community.disp_create_votecast(cid, vote, int(time.time()))
break

def convert_db_channel_to_json(self, channel):
return {"id": channel[0], "dispersy_cid": channel[1].encode('hex'), "name": channel[2], "description": channel[3],
"votes": channel[5], "torrents": channel[4], "spam": channel[6], "modified": channel[8],
"subscribed": (channel[7] == VOTE_SUBSCRIBE)}


class ChannelsEndpoint(BaseChannelsEndpoint):
"""
Expand Down Expand Up @@ -153,25 +146,82 @@ def render_DELETE(self, request):

class ChannelsDiscoveredEndpoint(BaseChannelsEndpoint):
"""
A GET request to this endpoint returns all channels discovered in Tribler.
This class is responsible for requests regarding the subscriptions to channels.
"""
def getChild(self, path, request):
return ChannelsDiscoveredSpecificEndpoint(self.session, path)

def render_GET(self, request):
"""
A GET request to this endpoint returns all channels discovered in Tribler.
Example GET response:
{
"channels": [{
"id": 3,
"dispersy_cid": "da69aaad39ccf468aba2ab9177d5f8d8160135e6",
"name": "My fancy channel",
"description": "A description of this fancy channel",
"subscribed": False,
"votes": 23,
"torrents": 3,
"spam": 5,
"modified": 14598395,
}, ...]
}
"""
all_channels_db = self.channel_db_handler.getAllChannels()
results_json = [convert_db_channel_to_json(channel) for channel in all_channels_db]
return json.dumps({"channels": results_json})


class ChannelsDiscoveredSpecificEndpoint(BaseChannelsEndpoint):
"""
This class is responsible for dispatching requests to perform operations in a specific discovered channel.
"""

def __init__(self, session, cid):
BaseChannelsEndpoint.__init__(self, session)

child_handler_dict = {"torrents": ChannelTorrentsEndpoint}
for path, child_cls in child_handler_dict.iteritems():
self.putChild(path, child_cls(session, bytes(cid.decode('hex'))))


class ChannelTorrentsEndpoint(BaseChannelsEndpoint):
"""
A GET request to this endpoint returns all discovered torrents in a specific channel. The size of the torrent is
in number of bytes. The last_tracker_check value will be 0 if we did not check the tracker state of the torrent yet.
Example GET response:
{
"channels": [{
"id": 3,
"dispersy_cid": "da69aaad39ccf468aba2ab9177d5f8d8160135e6",
"name": "My fancy channel",
"description": "A description of this fancy channel",
"subscribed": False,
"votes": 23,
"torrents": 3,
"spam": 5,
"modified": 14598395,
"torrents": [{
"id": 4,
"infohash": "97d2d8f5d37e56cfaeaae151d55f05b077074779",
"name": "Ubuntu-16.04-desktop-amd64",
"size": 8592385,
"category": "other",
"num_seeders": 42,
"num_leechers": 184,
"last_tracker_check": 1463176959
}, ...]
}
"""

def __init__(self, session, cid):
BaseChannelsEndpoint.__init__(self, session)
self.cid = cid

def render_GET(self, request):
all_channels_db = self.channel_db_handler.getAllChannels()
results_json = [convert_db_channel_to_json(channel) for channel in all_channels_db]
return json.dumps({"channels": results_json})
request.setHeader('Content-Type', 'text/json')
channel_info = self.get_channel_from_db(self.cid)
if channel_info is None:
return ChannelTorrentsEndpoint.return_404(request)

torrent_db_columns = ['Torrent.torrent_id', 'infohash', 'Torrent.name', 'length',
'Torrent.category', 'num_seeders', 'num_leechers', 'last_tracker_check']
results_local_torrents_channel = self.channel_db_handler\
.getTorrentsFromChannelId(channel_info[0], True, torrent_db_columns)

results_json = [convert_db_torrent_to_json(torrent_result) for torrent_result in results_local_torrents_channel]
return json.dumps({"torrents": results_json})
3 changes: 2 additions & 1 deletion Tribler/Core/Modules/restapi/search_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ def render_GET(self, request):
results_dict = {"keywords": keywords, "result_list": results_local_channels}
self.session.notifier.notify(SIGNAL_CHANNEL, SIGNAL_ON_SEARCH_RESULTS, None, results_dict)

torrent_db_columns = ['T.torrent_id', 'infohash', 'T.name', 'length', 'category', 'num_seeders', 'num_leechers']
torrent_db_columns = ['T.torrent_id', 'infohash', 'T.name', 'length', 'category',
'num_seeders', 'num_leechers', 'last_tracker_check']
results_local_torrents = self.torrent_db_handler.searchNames(keywords, keys=torrent_db_columns, doSort=False)
results_dict = {"keywords": keywords, "result_list": results_local_torrents}
self.session.notifier.notify(SIGNAL_TORRENT, SIGNAL_ON_SEARCH_RESULTS, None, results_dict)
Expand Down
8 changes: 5 additions & 3 deletions Tribler/Core/Modules/restapi/util.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
This file contains some utility methods that are used by the API.
"""
from Tribler.Core.Modules.restapi import VOTE_SUBSCRIBE


def convert_db_channel_to_json(channel):
Expand All @@ -9,12 +10,13 @@ def convert_db_channel_to_json(channel):
"""
return {"id": channel[0], "dispersy_cid": channel[1].encode('hex'), "name": channel[2], "description": channel[3],
"votes": channel[5], "torrents": channel[4], "spam": channel[6], "modified": channel[8],
"subscribed": (channel[7] == 2)}
"subscribed": (channel[7] == VOTE_SUBSCRIBE)}


def convert_db_torrent_to_json(torrent):
"""
This method converts a torrent in the database to a JSON dictionary.
"""
return {"id": torrent[0], "infohash": torrent[1].encode('hex'), "name": torrent[2], "length": torrent[3],
"category": torrent[4], "num_seeders": torrent[5] or 0, "num_leechers": torrent[6] or 0}
return {"id": torrent[0], "infohash": torrent[1].encode('hex'), "name": torrent[2], "size": torrent[3],
"category": torrent[4], "num_seeders": torrent[5] or 0, "num_leechers": torrent[6] or 0,
"last_tracker_check": torrent[7] or 0}
33 changes: 33 additions & 0 deletions Tribler/Test/Core/Modules/RestApi/test_channels_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ def insert_channel_in_db(self, dispersy_cid, peer_id, name, description):
def vote_for_channel(self, cid, vote_time):
self.votecast_db_handler.on_votes_from_dispersy([[cid, None, 'random', 2, vote_time]])

def insert_torrents_into_channel(self, torrent_list):
self.channel_db_handler.on_torrents_from_dispersy(torrent_list)


class TestChannelsEndpoint(AbstractTestChannelsEndpoint):

Expand Down Expand Up @@ -88,6 +91,36 @@ def test_get_discovered_channels(self):
return self.do_request('channels/discovered', expected_code=200).addCallback(self.verify_channels)


class TestChannelTorrentsEndpoint(AbstractTestChannelsEndpoint):

@deferred(timeout=10)
def test_get_torrents_in_channel_invalid_cid(self):
"""
Testing whether the API returns error 404 if a non-existent channel is queried for torrents
"""
self.should_check_equality = False
return self.do_request('channels/discovered/abcd/torrents', expected_code=404)

@deferred(timeout=10)
def test_get_torrents_in_channel(self):
"""
Testing whether the API returns inserted channels when fetching discovered channels
"""
def verify_torrents(torrents):
torrents_json = json.loads(torrents)
self.assertEqual(len(torrents_json['torrents']), 1)
self.assertEqual(torrents_json['torrents'][0]['infohash'], 'a' * 40)

self.should_check_equality = False
channel_id = self.insert_channel_in_db('rand', 42, 'Test channel', 'Test description')

torrent_list = [[channel_id, 1, 1, ('a' * 40).decode('hex'), 1460000000, "ubuntu-torrent.iso", [], []]]
self.insert_torrents_into_channel(torrent_list)

return self.do_request('channels/discovered/%s/torrents' % 'rand'.encode('hex'), expected_code=200)\
.addCallback(verify_torrents)


class TestChannelsSubscriptionEndpoint(AbstractTestChannelsEndpoint):

def setUp(self, autoload_discovery=True):
Expand Down
26 changes: 24 additions & 2 deletions doc/Tribler REST API.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ curl -X PUT http://localhost:8085/mychannel/rssfeeds/http%3A%2F%2Frssfeed.com%2F
| Endpoint | Description |
| ---- | --------------- |
| GET /channels/discovered | Get all discovered channels in Tribler |
| GET /channels/discovered/{channelcid}/torrents | Get all discovered torrents in a specific channel |
| GET /channels/subscribed | Get the channels you are subscribed to |
| PUT /channels/subscribed/{channelid} | Subscribe to a channel |
| DELETE /channels/subscribed/{channelid} | Unsubscribe from a channel |
| PUT /channels/subscribed/{channelcid} | Subscribe to a channel |
| DELETE /channels/subscribed/{channelcid} | Unsubscribe from a channel |

### My Channel

Expand Down Expand Up @@ -80,6 +81,27 @@ Returns all discovered channels in Tribler.
}
```

## `GET /channels/discovered/{channelcid}/torrents`

Returns all discovered torrents in a specific channel. The size of the torrent is in number of bytes. The last_tracker_check value will be 0 if we did not check the tracker state of the torrent yet.

### Example response

```json
{
"torrents": [{
"id": 4,
"infohash": "97d2d8f5d37e56cfaeaae151d55f05b077074779",
"name": "Ubuntu-16.04-desktop-amd64",
"size": 8592385,
"category": "other",
"num_seeders": 42,
"num_leechers": 184,
"last_tracker_check": 1463176959
}, ...]
}
```

## `GET /channels/subscribed`

Returns all the channels you are subscribed to.
Expand Down

0 comments on commit cdd22cd

Please sign in to comment.