Skip to content

Commit

Permalink
Merge pull request #2175 from brussee/my-channel
Browse files Browse the repository at this point in the history
Create channel api endpoint
  • Loading branch information
whirm committed May 18, 2016
2 parents d771012 + 3305e76 commit f2688ed
Show file tree
Hide file tree
Showing 14 changed files with 415 additions and 59 deletions.
41 changes: 30 additions & 11 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
*.bak
# Python
*.pyc
*.pyo
*~

# Apple
.DS_Store
.ropeproject

.*project
local.properties
*.bak
*~
/*.log
/.Tribler
/.coverage
/.dir-locals.el
/.gdb_history
/.idea/
**.idea/
/.noseids
/.project
/.pydevproject
/Tribler/.project
/Tribler/.pydevproject
/TAGS
/bootstraptribler.txt
/core
Expand All @@ -33,10 +34,28 @@ memorydump.out
test_file
test_file.torrent

# pycharm
**.idea/

# Ignore nosetest coverage output
cover/
htmlcov/
.coverage

# Android
*.apk

/android/TriblerService/service/typescript
/android/TriblerService/service/ffmpeg
/android/TriblerService/dist

/android/TriblerApp/app/src/main/jniLibs
/android/TriblerApp/app/src/main/jni/include/python2.7
/android/TriblerApp/app/src/main/jni/lib
/android/TriblerApp/app/src/main/assets/private.mp3

# Android Studio
/android/TriblerApp/app/*.iml
/android/TriblerApp/*.iml
/android/TriblerApp/app/build
/android/TriblerApp/build
/android/TriblerApp/.gradle
/android/TriblerApp/captures

8 changes: 8 additions & 0 deletions Tribler/Core/Modules/channel/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ def channel_id(self):
def name(self):
return self._channel_community.get_channel_name()

@property
def description(self):
return self._channel_community.get_channel_description()

@property
def mode(self):
return self._channel_community.get_channel_mode()

def get_rss_feed_url_list(self):
return [url for url in self._rss_feed_dict.iterkeys()]

Expand Down
7 changes: 5 additions & 2 deletions Tribler/Core/Modules/channel/channel_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from Tribler.dispersy.util import blocking_call_on_reactor_thread, call_on_reactor_thread
from Tribler.community.channel.community import ChannelCommunity

from Tribler.Core.exceptions import DuplicateChannelNameError
from Tribler.Core.Modules.channel.channel import ChannelObject


Expand Down Expand Up @@ -61,6 +62,8 @@ def create_channel(self, name, description, mode, rss_url=None):
:param description: Description of the Channel.
:param mode: Mode of the Channel ('open', 'semi-open', or 'closed').
:param rss_url: RSS URL for the Channel.
:return: Channel ID
:raises DuplicateChannelNameError if name already exists
"""
assert isinstance(name, basestring), u"name is not a basestring: %s" % type(name)
assert isinstance(description, basestring), u"description is not a basestring: %s" % type(description)
Expand All @@ -70,8 +73,7 @@ def create_channel(self, name, description, mode, rss_url=None):
# if two channels have the same name, this will not work
for channel_object in self._channel_list:
if name == channel_object.name:
self._logger.error(u"Channel name already exists: %s", name)
return
raise DuplicateChannelNameError(u"Channel name already exists: %s" % name)

channel_mode = self._channel_mode_map[mode]
community = ChannelCommunity.create_community(self.dispersy, self.session.dispersy_member,
Expand All @@ -90,6 +92,7 @@ def create_channel(self, name, description, mode, rss_url=None):
channel_obj.create_rss_feed(rss_url)

self._logger.debug(u"creating channel '%s', %s", channel_obj.name, hexlify(community.cid))
return channel_obj.channel_id

def get_my_channel(self, channel_id):
"""
Expand Down
78 changes: 60 additions & 18 deletions Tribler/Core/Modules/restapi/my_channel_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from Tribler.Core.CacheDB.sqlitecachedb import str2bin

from Tribler.Core.simpledefs import NTFY_CHANNELCAST
from Tribler.Core.exceptions import DuplicateChannelNameError


class MyChannelBaseEndpoint(resource.Resource):
Expand Down Expand Up @@ -36,31 +37,30 @@ class MyChannelEndpoint(MyChannelBaseEndpoint):
This endpoint is responsible for handing all requests regarding your channel such as getting and updating
torrents, playlists and rss-feeds.
"""

def __init__(self, session):
MyChannelBaseEndpoint.__init__(self, session)
child_handler_dict = {"overview": MyChannelOverviewEndpoint, "torrents": MyChannelTorrentsEndpoint,
"rssfeeds": MyChannelRssFeedsEndpoint, "playlists": MyChannelPlaylistsEndpoint,
child_handler_dict = {"torrents": MyChannelTorrentsEndpoint,
"rssfeeds": MyChannelRssFeedsEndpoint,
"playlists": MyChannelPlaylistsEndpoint,
"recheckfeeds": MyChannelRecheckFeedsEndpoint}
for path, child_cls in child_handler_dict.iteritems():
self.putChild(path, child_cls(self.session))


class MyChannelOverviewEndpoint(MyChannelBaseEndpoint):
"""
Return the name, description and identifier of your channel.
This endpoint returns a 404 HTTP response if you have not created a channel (yet).
Example response:
{
"overview": {
"name": "My Tribler channel",
"description": "A great collection of open-source movies",
"identifier": "4a9cfc7ca9d15617765f4151dd9fae94c8f3ba11"
}
}
"""

def render_GET(self, request):
"""
Return the name, description and identifier of your channel.
This endpoint returns a 404 HTTP response if you have not created a channel (yet).
Example response:
{
"overview": {
"name": "My Tribler channel",
"description": "A great collection of open-source movies",
"identifier": "4a9cfc7ca9d15617765f4151dd9fae94c8f3ba11"
}
}
"""
my_channel_id = self.channel_db_handler.getMyChannelId()
if my_channel_id is None:
return MyChannelBaseEndpoint.return_404(request)
Expand All @@ -70,6 +70,48 @@ def render_GET(self, request):
return json.dumps({'overview': {'identifier': my_channel[1].encode('hex'), 'name': my_channel[2],
'description': my_channel[3]}})

def render_PUT(self, request):
"""
Create your own new channel.
Example request:
{
"name": "John Smit's channel",
"description": "Video's of my cat",
"mode": "open" or "semi-open" or "closed" (default)
}
"""
parameters = http.parse_qs(request.content.read(), 1)

if 'name' not in parameters or len(parameters['name']) == 0:
request.setResponseCode(http.BAD_REQUEST)
return json.dumps({"error": "name parameter missing"})

if 'description' not in parameters or len(parameters['description']) == 0:
request.setResponseCode(http.BAD_REQUEST)
return json.dumps({"error": "description parameter missing"})

if 'mode' not in parameters or len(parameters['mode']) == 0:
mode = u'closed'
else:
mode = parameters['mode'][0]

try:
channel_id = self.session.create_channel(parameters['name'][0], parameters['description'][0], mode)
except DuplicateChannelNameError as ex:
request.setResponseCode(http.INTERNAL_SERVER_ERROR)
request.setHeader('Content-Type', 'text/json')
return json.dumps({
u"error": {
u"handled": True,
u"code": ex.__class__.__name__,
u"message": ex.message
}
})

request.setHeader('Content-Type', 'text/json')
return json.dumps({"added": channel_id})


class MyChannelTorrentsEndpoint(MyChannelBaseEndpoint):
"""
Expand Down
39 changes: 37 additions & 2 deletions Tribler/Core/Modules/restapi/rest_manager.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import json
import logging
from traceback import format_tb

from twisted.internet import reactor
from twisted.internet.defer import maybeDeferred
from twisted.web import server
from twisted.python.compat import intToBytes
from twisted.web import server, http

from Tribler.Core.Modules.restapi.root_endpoint import RootEndpoint
from Tribler.dispersy.taskmanager import TaskManager
Expand All @@ -24,10 +27,42 @@ def start(self):
Starts the HTTP API with the listen port as specified in the session configuration.
"""
self.site = reactor.listenTCP(self.session.get_http_api_port(),
server.Site(RootEndpoint(self.session)))
server.Site(resource=RootEndpoint(self.session),
requestFactory=RESTRequest))

def stop(self):
"""
Stop the HTTP API and return a deferred that fires when the server has shut down.
"""
return maybeDeferred(self.site.stopListening)


class RESTRequest(server.Request):
"""
This class gracefully takes care of unhandled exceptions raised during the processing of any request.
"""
defaultContentType = b"text/json"

def __init__(self, *args, **kw):
server.Request.__init__(self, *args, **kw)
self._logger = logging.getLogger(self.__class__.__name__)

def processingFailed(self, reason):
self._logger.error(reason)
response = {
u"error": {
u"handled": False,
u"code": reason.value.__class__.__name__,
u"message": reason.value.message
}
}
if self.site.displayTracebacks:
response[u"error"][u"trace"] = format_tb(reason.getTracebackObject())

body = json.dumps(response)
self.setResponseCode(http.INTERNAL_SERVER_ERROR)
self.setHeader(b'content-type', self.defaultContentType)
self.setHeader(b'content-length', intToBytes(len(body)))
self.write(body)
self.finish()
return reason
8 changes: 4 additions & 4 deletions Tribler/Core/Session.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,16 +637,16 @@ def search_remote_channels(self, keywords):
raise OperationNotEnabledByConfigurationException("channel_search is not enabled")
self.lm.search_manager.search_for_channels(keywords)

def create_channel(self, name, description, mode=u'open'):
def create_channel(self, name, description, mode=u'closed'):
"""
Creates a new Channel.
:param name: Name of the Channel.
:param description: Description of the Channel.
:param mode: Mode of the Channel ('open', 'semi-open', or 'closed').
:return: Channel ID
:raises DuplicateChannelNameError if name already exists
"""
if not self.get_enable_channel_search():
raise OperationNotEnabledByConfigurationException("channel_search is not enabled")
self.lm.channel_manager.create_channel(name, description, mode)
return self.lm.channel_manager.create_channel(name, description, mode)

def check_torrent_health(self, infohash):
"""
Expand Down
9 changes: 9 additions & 0 deletions Tribler/Core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ def __init__(self, msg=None):
TriblerException.__init__(self, msg)


class DuplicateChannelNameError(TriblerException):

""" The Channel name already exists in the ChannelManager channel list,
i.e., one of your own Channels with the same name already exists. """

pass


class DuplicateDownloadException(TriblerException):

""" The Download already exists in the Session, i.e., a Download for
Expand All @@ -65,6 +73,7 @@ class TorrentDefNotFinalizedException(TriblerException):
def __init__(self, msg=None):
TriblerException.__init__(self, msg)


class TorrentFileException(TriblerException):

""" The torrent file that is used is corrupt or cannot be read. """
Expand Down
2 changes: 1 addition & 1 deletion Tribler/Test/Core/Modules/RestApi/base_api_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def parse_body(self, body):

def parse_response(self, response):
self.assertEqual(response.code, self.expected_response_code)
if response.code == 200 or response.code == 400:
if response.code in (200, 400, 500):
return readBody(response)
return succeed(None)

Expand Down

0 comments on commit f2688ed

Please sign in to comment.