def load_from_url(url): """ Create a TorrentDef with information from a remote source. :param url: The HTTP/HTTPS url where to fetch the torrent info from. """ # Class method, no locking required def _on_response(data): return TorrentDef.load_from_memory(data) deferred = http_get(url) deferred.addCallback(_on_response) return deferred
def test_http_get_expired(self): uri = "https://expired.badssl.com" def cbResponse(_): self.fail("Error was expected.") def cbErrorResponse(response): self.assertIsNotNone(response) http_deferred = http_get(uri) http_deferred.addCallback(cbResponse) http_deferred.addErrback(cbErrorResponse) return http_deferred
def load_from_url(url): """ Load a BT .torrent or Tribler .tstream file from the URL and convert it into a TorrentDef. @param url URL @return Deferred """ # Class method, no locking required def _on_response(data): return TorrentDef.load_from_memory(data) deferred = http_get(url) deferred.addCallback(_on_response) return deferred
def test_http_get_with_redirect(self): """ Test if http_get is working properly if url redirects to a magnet link. """ def on_callback(response): self.assertEqual(response, magnet_link) # Setup a redirect server which redirects to a magnet link magnet_link = "magnet:?xt=urn:btih:DC4B96CF85A85CEEDB8ADC4B96CF85A85CEEDB8A" port = get_random_port() self.setUpHttpRedirectServer(port, magnet_link) test_url = "http://localhost:%d" % port http_deferred = http_get(test_url).addCallback(on_callback) return http_deferred
def check_new_version(self): def parse_body(body): if body is None: return version = json.loads(body)['name'][1:] if LooseVersion(version) > LooseVersion(version_id): self.session.notifier.notify(NTFY_NEW_VERSION, NTFY_INSERT, None, version) def on_request_error(failure): failure.trap(SchemeNotSupported, ConnectError, DNSLookupError) self._logger.error("Error when performing version check request: %s", failure) def on_response_error(failure): failure.trap(HttpError) self._logger.warning("Got response code %s when performing version check request", failure.value.response.code) deferred = http_get(VERSION_CHECK_URL) deferred.addErrback(on_response_error).addCallback(parse_body).addErrback(on_request_error) return deferred
def parse(self, url, cache): """ Parses a RSS feed. This methods supports RSS 2.0 and Media RSS. """ def on_rss_response(response): feed = feedparser.parse(response) feed_items = [] for item in feed.entries: # ignore the ones that we have seen before link = item.get(u'link', None) if link is None or cache.has(link): continue title = self._html2plaintext(item[u'title']).strip() description = self._html2plaintext( item.get(u'media_description', u'')).strip() torrent_url = item[u'link'] thumbnail_list = [] media_thumbnail_list = item.get(u'media_thumbnail', None) if media_thumbnail_list: for thumbnail in media_thumbnail_list: thumbnail_list.append(thumbnail[u'url']) # assemble the information parsed_item = { u'title': title, u'description': description, u'torrent_url': torrent_url, u'thumbnail_list': thumbnail_list } feed_items.append(parsed_item) return feed_items def on_rss_error(failure): self._logger.error("Error when fetching RSS feed: %s", failure) return http_get(str(url)).addCallbacks(on_rss_response, on_rss_error)
def render_GET(self, request): """ .. http:get:: /torrentinfo A GET request to this endpoint will return information from a torrent found at a provided URI. This URI can either represent a file location, a magnet link or a HTTP(S) url. - torrent: the URI of the torrent file that should be downloaded. This parameter is required. **Example request**: .. sourcecode:: none curl -X PUT http://localhost:8085/torrentinfo?torrent=file:/home/me/test.torrent **Example response**: .. sourcecode:: javascript {"metainfo": <torrent metainfo dictionary>} """ metainfo_deferred = Deferred() def on_got_metainfo(metainfo): if not isinstance(metainfo, dict) or 'info' not in metainfo: self._logger.warning( "Received metainfo is not a valid dictionary") request.setResponseCode(http.INTERNAL_SERVER_ERROR) request.write(json.dumps({"error": 'invalid response'})) self.finish_request(request) return infohash = hashlib.sha1(bencode(metainfo['info'])).digest() # Save the torrent to our store try: self.session.save_collected_torrent(infohash, bencode(metainfo)) except TypeError: # Note: in libtorrent 1.1.1, bencode throws a TypeError which is a known bug pass request.write( json.dumps({"metainfo": metainfo}, ensure_ascii=False)) self.finish_request(request) def on_metainfo_timeout(_): if not request.finished: request.setResponseCode(http.REQUEST_TIMEOUT) request.write(json.dumps({"error": "timeout"})) # If the above request.write failed, the request will have already been finished if not request.finished: self.finish_request(request) def on_lookup_error(failure): failure.trap(ConnectError, DNSLookupError, HttpError) request.setResponseCode(http.INTERNAL_SERVER_ERROR) request.write(json.dumps({"error": failure.getErrorMessage()})) self.finish_request(request) if 'uri' not in request.args or len(request.args['uri']) == 0: request.setResponseCode(http.BAD_REQUEST) return json.dumps({"error": "uri parameter missing"}) uri = unicode(request.args['uri'][0], 'utf-8') if uri.startswith('file:'): try: filename = url2pathname(uri[5:].replace("+", " ")) metainfo_deferred.callback(bdecode(fix_torrent(filename))) except TypeError: request.setResponseCode(http.INTERNAL_SERVER_ERROR) return json.dumps( {"error": "error while decoding torrent file"}) elif uri.startswith('http'): def _on_loaded(metadata): metainfo_deferred.callback(bdecode(metadata)) http_get(uri.encode('utf-8')).addCallback(_on_loaded).addErrback( on_lookup_error) elif uri.startswith('magnet'): infohash = parse_magnetlink(uri)[1] if infohash is None: request.setResponseCode(http.BAD_REQUEST) return json.dumps({"error": "missing infohash"}) if self.session.has_collected_torrent(infohash): try: tdef = TorrentDef.load_from_memory( self.session.get_collected_torrent(infohash)) except ValueError as exc: request.setResponseCode(http.INTERNAL_SERVER_ERROR) return json.dumps( {"error": "invalid torrent file: %s" % str(exc)}) on_got_metainfo(tdef.get_metainfo()) return NOT_DONE_YET self.session.lm.ltmgr.get_metainfo( uri, callback=metainfo_deferred.callback, timeout=20, timeout_callback=on_metainfo_timeout, notify=True) else: request.setResponseCode(http.BAD_REQUEST) return json.dumps({"error": "invalid uri"}) metainfo_deferred.addCallback(on_got_metainfo) return NOT_DONE_YET
def render_PUT(self, request): """ .. http:put:: /channels/discovered/(string: channelid)/torrents/http%3A%2F%2Ftest.com%2Ftest.torrent Add a torrent by magnet or url to your channel. Returns error 500 if something is wrong with the torrent file and DuplicateTorrentFileError if already added to your channel (except with magnet links). **Example request**: .. sourcecode:: none curl -X PUT http://localhost:8085/channels/discovered/abcdefg/torrents/ http%3A%2F%2Ftest.com%2Ftest.torrent --data "description=nice video" **Example response**: .. sourcecode:: javascript { "added": "http://test.com/test.torrent" } :statuscode 404: if your channel does not exist. :statuscode 500: if the specified torrent is already in your channel. """ my_key = self.session.trustchain_keypair my_channel_id = my_key.pub().key_to_bin() if self.is_chant_channel: if my_channel_id != self.cid: request.setResponseCode(http.NOT_ALLOWED) return json.dumps({ "error": "you can only add torrents to your own chant channel" }) channel = self.session.lm.mds.ChannelMetadata.get_channel_with_id( my_channel_id) else: channel = self.get_channel_from_db(self.cid) if channel is None: return BaseChannelsEndpoint.return_404(request) parameters = http.parse_qs(request.content.read(), 1) if 'description' not in parameters or len( parameters['description']) == 0: extra_info = {} else: extra_info = {'description': parameters['description'][0]} def _on_url_fetched(data): return TorrentDef.load_from_memory(data) def _on_magnet_fetched(meta_info): return TorrentDef.load_from_dict(meta_info) def _on_torrent_def_loaded(torrent_def): if self.is_chant_channel: # We have to get my channel again since we are in a different database session now with db_session: channel = self.session.lm.mds.get_my_channel() channel.add_torrent_to_channel(torrent_def, extra_info) else: channel = self.get_channel_from_db(self.cid) self.session.add_torrent_def_to_channel(channel[0], torrent_def, extra_info, forward=True) return self.path def _on_added(added): request.write(json.dumps({"added": added})) request.finish() def _on_add_failed(failure): failure.trap(ValueError, DuplicateTorrentFileError, SchemeNotSupported) self._logger.exception(failure.value) request.write( BaseChannelsEndpoint.return_500(self, request, failure.value)) request.finish() def _on_timeout(_): request.write( BaseChannelsEndpoint.return_500( self, request, RuntimeError("Metainfo timeout"))) request.finish() if self.path.startswith("http:") or self.path.startswith("https:"): self.deferred = http_get(self.path) self.deferred.addCallback(_on_url_fetched) if self.path.startswith("magnet:"): try: self.session.lm.ltmgr.get_metainfo( self.path, callback=self.deferred.callback, timeout=30, timeout_callback=_on_timeout, notify=True) except Exception as ex: self.deferred.errback(ex) self.deferred.addCallback(_on_magnet_fetched) self.deferred.addCallback(_on_torrent_def_loaded) self.deferred.addCallback(_on_added) self.deferred.addErrback(_on_add_failed) return NOT_DONE_YET
def render_PUT(self, request): """ .. http:put:: /mychannel/torrents Add a torrent file to your own channel. Returns error 500 if something is wrong with the torrent file and DuplicateTorrentFileError if already added to your channel. The torrent data is passed as base-64 encoded string. The description is optional. Option torrents_dir adds all .torrent files from a chosen directory Option recursive enables recursive scanning of the chosen directory for .torrent files **Example request**: .. sourcecode:: none curl -X PUT http://localhost:8085/mychannel/torrents --data "torrent=...&description=funny video" **Example response**: .. sourcecode:: javascript { "added": True } **Example request**: .. sourcecode:: none curl -X PUT http://localhost:8085/mychannel/torrents? --data "torrents_dir=some_dir&recursive=1" **Example response**: .. sourcecode:: javascript { "added": 13 } :statuscode 404: if your channel does not exist. :statuscode 500: if the passed torrent data is corrupt. """ my_channel = self.session.lm.mds.ChannelMetadata.get_my_channel() if not my_channel: request.setResponseCode(http.NOT_FOUND) return json.dumps({"error": "your channel has not been created yet"}) parameters = http.parse_qs(request.content.read(), 1) if 'description' not in parameters or not parameters['description']: extra_info = {} else: extra_info = {'description': parameters['description'][0]} def _on_url_fetched(data): return TorrentDef.load_from_memory(data) def _on_magnet_fetched(meta_info): return TorrentDef.load_from_dict(meta_info) def _on_torrent_def_loaded(torrent_def): with db_session: channel = self.session.lm.mds.get_my_channel() channel.add_torrent_to_channel(torrent_def, extra_info) return 1 def _on_added(added): request.write(json.dumps({"added": added})) request.finish() def _on_add_failed(failure): failure.trap(ValueError, DuplicateTorrentFileError, SchemeNotSupported) self._logger.exception(failure.value) request.write(self.return_500(request, failure.value)) request.finish() def _on_timeout(_): request.write(self.return_500(request, RuntimeError("Metainfo timeout"))) request.finish() # First, check whether we did upload a magnet link or URL if 'uri' in parameters and parameters['uri']: deferred = Deferred() uri = parameters['uri'][0] if uri.startswith("http:") or uri.startswith("https:"): deferred = http_get(uri) deferred.addCallback(_on_url_fetched) elif uri.startswith("magnet:"): try: self.session.lm.ltmgr.get_metainfo(uri, callback=deferred.callback, timeout=30, timeout_callback=_on_timeout, notify=True) except Exception as ex: deferred.errback(ex) deferred.addCallback(_on_magnet_fetched) else: request.setResponseCode(http.BAD_REQUEST) return json.dumps({"error": "unknown uri type"}) deferred.addCallback(_on_torrent_def_loaded) deferred.addCallback(_on_added) deferred.addErrback(_on_add_failed) return NOT_DONE_YET torrents_dir = None if 'torrents_dir' in parameters and parameters['torrents_dir'] > 0: torrents_dir = parameters['torrents_dir'][0] if not os.path.isabs(torrents_dir): request.setResponseCode(http.BAD_REQUEST) return json.dumps({"error": "the torrents_dir should point to a directory"}) recursive = False if 'recursive' in parameters and parameters['recursive'] > 0: recursive = parameters['recursive'][0] if not torrents_dir: request.setResponseCode(http.BAD_REQUEST) return json.dumps({"error": "the torrents_dir parameter should be provided when the recursive " "parameter is set"}) if torrents_dir: torrents_list, errors_list = my_channel.add_torrents_from_dir(torrents_dir, recursive) return json.dumps({"added": len(torrents_list), "errors": errors_list}) if 'torrent' not in parameters or not parameters['torrent']: request.setResponseCode(http.BAD_REQUEST) return json.dumps({"error": "torrent parameter missing"}) # Try to parse the torrent data try: torrent = base64.b64decode(parameters['torrent'][0]) torrent_def = TorrentDef.load_from_memory(torrent) except (TypeError, ValueError): request.setResponseCode(http.INTERNAL_SERVER_ERROR) return json.dumps({"error": "invalid torrent file"}) try: my_channel.add_torrent_to_channel(torrent_def, extra_info) except DuplicateTorrentFileError: request.setResponseCode(http.INTERNAL_SERVER_ERROR) return json.dumps({"error": "this torrent already exists in your channel"}) return json.dumps({"added": 1})
def render_PUT(self, request): """ .. http:put:: /channels/discovered/(string: channelid)/torrents/http%3A%2F%2Ftest.com%2Ftest.torrent Add a torrent by magnet or url to your channel. Returns error 500 if something is wrong with the torrent file and DuplicateTorrentFileError if already added to your channel (except with magnet links). **Example request**: .. sourcecode:: none curl -X PUT http://localhost:8085/channels/discovered/abcdefg/torrents/ http%3A%2F%2Ftest.com%2Ftest.torrent --data "description=nice video" **Example response**: .. sourcecode:: javascript { "added": "http://test.com/test.torrent" } :statuscode 404: if your channel does not exist. :statuscode 500: if the specified torrent is already in your channel. """ channel = self.get_channel_from_db(self.cid) if channel is None: return BaseChannelsEndpoint.return_404(request) parameters = http.parse_qs(request.content.read(), 1) if 'description' not in parameters or len(parameters['description']) == 0: extra_info = {} else: extra_info = {'description': parameters['description'][0]} def _on_url_fetched(data): return TorrentDef.load_from_memory(data) def _on_magnet_fetched(meta_info): return TorrentDef.load_from_dict(meta_info) @blocking_call_on_reactor_thread def _on_torrent_def_loaded(torrent_def): self.session.add_torrent_def_to_channel(channel[0], torrent_def, extra_info, forward=True) return self.path def _on_added(added): request.write(json.dumps({"added": added})) request.finish() def _on_add_failed(failure): failure.trap(ValueError, DuplicateTorrentFileError) self._logger.exception(failure.value) request.write(BaseChannelsEndpoint.return_500(self, request, failure.value)) request.finish() if self.path.startswith("http:") or self.path.startswith("https:"): self.deferred = http_get(self.path) self.deferred.addCallback(_on_url_fetched) if self.path.startswith("magnet:"): try: self.session.lm.ltmgr.get_metainfo(self.path, callback=self.deferred.callback, timeout=30, timeout_callback=self.deferred.errback, notify=True) except Exception as ex: self.deferred.errback(ex) self.deferred.addCallback(_on_magnet_fetched) self.deferred.addCallback(_on_torrent_def_loaded) self.deferred.addCallback(_on_added) self.deferred.addErrback(_on_add_failed) return NOT_DONE_YET
def render_GET(self, request): """ .. http:get:: /torrentinfo A GET request to this endpoint will return information from a torrent found at a provided URI. This URI can either represent a file location, a magnet link or a HTTP(S) url. - torrent: the URI of the torrent file that should be downloaded. This parameter is required. **Example request**: .. sourcecode:: none curl -X PUT http://localhost:8085/torrentinfo?torrent=file:/home/me/test.torrent **Example response**: .. sourcecode:: javascript {"metainfo": <torrent metainfo dictionary>} """ def on_got_metainfo(metainfo): if not isinstance(metainfo, dict) or 'info' not in metainfo: self._logger.warning( "Received metainfo is not a valid dictionary") request.setResponseCode(http.INTERNAL_SERVER_ERROR) request.write(json.dumps({"error": 'invalid response'})) self.finish_request(request) return # TODO(Martijn): store the stuff in a database!!! infohash = hashlib.sha1(bencode(metainfo['info'])).digest() # Check if the torrent is already in the downloads metainfo['download_exists'] = infohash in self.session.lm.downloads encoded_metainfo = hexlify(json.dumps(metainfo, ensure_ascii=False)) request.write(json.dumps({"metainfo": encoded_metainfo})) self.finish_request(request) def on_metainfo_timeout(_): if not request.finished: request.setResponseCode(http.REQUEST_TIMEOUT) request.write(json.dumps({"error": "timeout"})) # If the above request.write failed, the request will have already been finished if not request.finished: self.finish_request(request) def on_lookup_error(failure): failure.trap(ConnectError, DNSLookupError, HttpError, ConnectionLost) request.setResponseCode(http.INTERNAL_SERVER_ERROR) request.write( json.dumps( {"error": unichar_string(failure.getErrorMessage())})) self.finish_request(request) def _on_loaded(response): if response.startswith('magnet'): _, infohash, _ = parse_magnetlink(response) if infohash: self.session.lm.ltmgr.get_metainfo( response, callback=metainfo_deferred.callback, timeout=20, timeout_callback=on_metainfo_timeout, notify=True) return metainfo_deferred.callback(bdecode(response)) def on_mdblob(filename): try: with open(filename, 'rb') as f: serialized_data = f.read() payload = read_payload(serialized_data) if payload.metadata_type not in [ REGULAR_TORRENT, CHANNEL_TORRENT ]: request.setResponseCode(http.INTERNAL_SERVER_ERROR) return json.dumps({"error": "Non-torrent metadata type"}) magnet = payload.get_magnet() except InvalidSignatureException: request.setResponseCode(http.INTERNAL_SERVER_ERROR) return json.dumps( {"error": "metadata has incorrect signature"}) else: return on_magnet(magnet) def on_file(): try: filename = url2pathname(uri[5:].encode('utf-8') if isinstance( uri, text_type) else uri[5:]) if filename.endswith(BLOB_EXTENSION): return on_mdblob(filename) metainfo_deferred.callback(bdecode(fix_torrent(filename))) return NOT_DONE_YET except TypeError: request.setResponseCode(http.INTERNAL_SERVER_ERROR) return json.dumps( {"error": "error while decoding torrent file"}) def on_magnet(mlink=None): infohash = parse_magnetlink(mlink or uri)[1] if infohash is None: request.setResponseCode(http.BAD_REQUEST) return json.dumps({"error": "missing infohash"}) self.session.lm.ltmgr.get_metainfo( mlink or uri, callback=metainfo_deferred.callback, timeout=20, timeout_callback=on_metainfo_timeout, notify=True) return NOT_DONE_YET metainfo_deferred = Deferred() metainfo_deferred.addCallback(on_got_metainfo) if 'uri' not in request.args or not request.args['uri']: request.setResponseCode(http.BAD_REQUEST) return json.dumps({"error": "uri parameter missing"}) uri = cast_to_unicode_utf8(request.args['uri'][0]) if uri.startswith('file:'): return on_file() elif uri.startswith('http'): http_get(uri.encode('utf-8')).addCallback(_on_loaded).addErrback( on_lookup_error) elif uri.startswith('magnet'): return on_magnet() else: request.setResponseCode(http.BAD_REQUEST) return json.dumps({"error": "invalid uri"}) return NOT_DONE_YET
def render_GET(self, request): """ .. http:get:: /torrentinfo A GET request to this endpoint will return information from a torrent found at a provided URI. This URI can either represent a file location, a magnet link or a HTTP(S) url. - torrent: the URI of the torrent file that should be downloaded. This parameter is required. **Example request**: .. sourcecode:: none curl -X PUT http://localhost:8085/torrentinfo?torrent=file:/home/me/test.torrent **Example response**: .. sourcecode:: javascript {"metainfo": <torrent metainfo dictionary>} """ def on_got_metainfo(metainfo): if not isinstance(metainfo, dict) or 'info' not in metainfo: self._logger.warning("Received metainfo is not a valid dictionary") request.setResponseCode(http.INTERNAL_SERVER_ERROR) request.write(json.dumps({"error": 'invalid response'})) self.finish_request(request) return # TODO(Martijn): store the stuff in a database!!! infohash = hashlib.sha1(bencode(metainfo['info'])).digest() # Check if the torrent is already in the downloads metainfo['download_exists'] = infohash in self.session.lm.downloads encoded_metainfo = hexlify(json.dumps(metainfo, ensure_ascii=False)) request.write(json.dumps({"metainfo": encoded_metainfo})) self.finish_request(request) def on_metainfo_timeout(_): if not request.finished: request.setResponseCode(http.REQUEST_TIMEOUT) request.write(json.dumps({"error": "timeout"})) # If the above request.write failed, the request will have already been finished if not request.finished: self.finish_request(request) def on_lookup_error(failure): failure.trap(ConnectError, DNSLookupError, HttpError, ConnectionLost) request.setResponseCode(http.INTERNAL_SERVER_ERROR) request.write(json.dumps({"error": unichar_string(failure.getErrorMessage())})) self.finish_request(request) def _on_loaded(response): if response.startswith('magnet'): _, infohash, _ = parse_magnetlink(response) if infohash: self.session.lm.ltmgr.get_metainfo(response, callback=metainfo_deferred.callback, timeout=20, timeout_callback=on_metainfo_timeout, notify=True) return metainfo_deferred.callback(bdecode(response)) def on_mdblob(filename): try: with open(filename, 'rb') as f: serialized_data = f.read() payload = read_payload(serialized_data) if payload.metadata_type not in [REGULAR_TORRENT, CHANNEL_TORRENT]: request.setResponseCode(http.INTERNAL_SERVER_ERROR) return json.dumps({"error": "Non-torrent metadata type"}) magnet = payload.get_magnet() except InvalidSignatureException: request.setResponseCode(http.INTERNAL_SERVER_ERROR) return json.dumps({"error": "metadata has incorrect signature"}) else: return on_magnet(magnet) def on_file(): try: filename = url2pathname(uri[5:].encode('utf-8') if isinstance(uri, text_type) else uri[5:]) if filename.endswith(BLOB_EXTENSION): return on_mdblob(filename) metainfo_deferred.callback(bdecode(fix_torrent(filename))) return NOT_DONE_YET except TypeError: request.setResponseCode(http.INTERNAL_SERVER_ERROR) return json.dumps({"error": "error while decoding torrent file"}) def on_magnet(mlink=None): infohash = parse_magnetlink(mlink or uri)[1] if infohash is None: request.setResponseCode(http.BAD_REQUEST) return json.dumps({"error": "missing infohash"}) self.session.lm.ltmgr.get_metainfo(mlink or uri, callback=metainfo_deferred.callback, timeout=20, timeout_callback=on_metainfo_timeout, notify=True) return NOT_DONE_YET metainfo_deferred = Deferred() metainfo_deferred.addCallback(on_got_metainfo) if 'uri' not in request.args or not request.args['uri']: request.setResponseCode(http.BAD_REQUEST) return json.dumps({"error": "uri parameter missing"}) uri = cast_to_unicode_utf8(request.args['uri'][0]) if uri.startswith('file:'): return on_file() elif uri.startswith('http'): http_get(uri.encode('utf-8')).addCallback(_on_loaded).addErrback(on_lookup_error) elif uri.startswith('magnet'): return on_magnet() else: request.setResponseCode(http.BAD_REQUEST) return json.dumps({"error": "invalid uri"}) return NOT_DONE_YET
def render_PUT(self, request): """ .. http:put:: /mychannel/torrents Add a torrent file to your own channel. Returns error 500 if something is wrong with the torrent file and DuplicateTorrentFileError if already added to your channel. The torrent data is passed as base-64 encoded string. The description is optional. Option torrents_dir adds all .torrent files from a chosen directory Option recursive enables recursive scanning of the chosen directory for .torrent files **Example request**: .. sourcecode:: none curl -X PUT http://localhost:8085/mychannel/torrents --data "torrent=...&description=funny video" **Example response**: .. sourcecode:: javascript { "added": True } **Example request**: .. sourcecode:: none curl -X PUT http://localhost:8085/mychannel/torrents? --data "torrents_dir=some_dir&recursive=1" **Example response**: .. sourcecode:: javascript { "added": 13 } :statuscode 404: if your channel does not exist. :statuscode 500: if the passed torrent data is corrupt. """ my_channel = self.session.lm.mds.ChannelMetadata.get_my_channel() if not my_channel: request.setResponseCode(http.NOT_FOUND) return json.dumps( {"error": "your channel has not been created yet"}) parameters = http.parse_qs(request.content.read(), 1) if 'description' not in parameters or not parameters['description']: extra_info = {} else: extra_info = {'description': parameters['description'][0]} def _on_url_fetched(data): return TorrentDef.load_from_memory(data) def _on_magnet_fetched(meta_info): return TorrentDef.load_from_dict(meta_info) def _on_torrent_def_loaded(torrent_def): with db_session: channel = self.session.lm.mds.get_my_channel() channel.add_torrent_to_channel(torrent_def, extra_info) return 1 def _on_added(added): request.write(json.dumps({"added": added})) request.finish() def _on_add_failed(failure): failure.trap(ValueError, DuplicateTorrentFileError, SchemeNotSupported) self._logger.exception(failure.value) request.write(self.return_500(request, failure.value)) request.finish() def _on_timeout(_): request.write( self.return_500(request, RuntimeError("Metainfo timeout"))) request.finish() # First, check whether we did upload a magnet link or URL if 'uri' in parameters and parameters['uri']: deferred = Deferred() uri = parameters['uri'][0] if uri.startswith("http:") or uri.startswith("https:"): deferred = http_get(uri) deferred.addCallback(_on_url_fetched) elif uri.startswith("magnet:"): try: self.session.lm.ltmgr.get_metainfo( uri, callback=deferred.callback, timeout=30, timeout_callback=_on_timeout, notify=True) except Exception as ex: deferred.errback(ex) deferred.addCallback(_on_magnet_fetched) else: request.setResponseCode(http.BAD_REQUEST) return json.dumps({"error": "unknown uri type"}) deferred.addCallback(_on_torrent_def_loaded) deferred.addCallback(_on_added) deferred.addErrback(_on_add_failed) return NOT_DONE_YET torrents_dir = None if 'torrents_dir' in parameters and parameters['torrents_dir'] > 0: torrents_dir = parameters['torrents_dir'][0] if not os.path.isabs(torrents_dir): request.setResponseCode(http.BAD_REQUEST) return json.dumps( {"error": "the torrents_dir should point to a directory"}) recursive = False if 'recursive' in parameters and parameters['recursive'] > 0: recursive = parameters['recursive'][0] if not torrents_dir: request.setResponseCode(http.BAD_REQUEST) return json.dumps({ "error": "the torrents_dir parameter should be provided when the recursive " "parameter is set" }) if torrents_dir: torrents_list, errors_list = my_channel.add_torrents_from_dir( torrents_dir, recursive) return json.dumps({ "added": len(torrents_list), "errors": errors_list }) if 'torrent' not in parameters or not parameters['torrent']: request.setResponseCode(http.BAD_REQUEST) return json.dumps({"error": "torrent parameter missing"}) # Try to parse the torrent data try: torrent = base64.b64decode(parameters['torrent'][0]) torrent_def = TorrentDef.load_from_memory(torrent) except (TypeError, ValueError): request.setResponseCode(http.INTERNAL_SERVER_ERROR) return json.dumps({"error": "invalid torrent file"}) try: my_channel.add_torrent_to_channel(torrent_def, extra_info) except DuplicateTorrentFileError: request.setResponseCode(http.INTERNAL_SERVER_ERROR) return json.dumps( {"error": "this torrent already exists in your channel"}) return json.dumps({"added": 1})
def render_PUT(self, request): """ .. http:put:: /channels/discovered/(string: channelid)/torrents/http%3A%2F%2Ftest.com%2Ftest.torrent Add a torrent by magnet or url to your channel. Returns error 500 if something is wrong with the torrent file and DuplicateTorrentFileError if already added to your channel (except with magnet links). **Example request**: .. sourcecode:: none curl -X PUT http://localhost:8085/channels/discovered/abcdefg/torrents/ http%3A%2F%2Ftest.com%2Ftest.torrent --data "description=nice video" **Example response**: .. sourcecode:: javascript { "added": "http://test.com/test.torrent" } :statuscode 404: if your channel does not exist. :statuscode 500: if the specified torrent is already in your channel. """ my_key = self.session.trustchain_keypair my_channel_id = my_key.pub().key_to_bin() if self.is_chant_channel: if my_channel_id != self.cid: request.setResponseCode(http.NOT_ALLOWED) return json.dumps({"error": "you can only add torrents to your own chant channel"}) channel = self.session.lm.mds.ChannelMetadata.get_channel_with_id(my_channel_id) else: channel = self.get_channel_from_db(self.cid) if channel is None: return BaseChannelsEndpoint.return_404(request) parameters = http.parse_qs(request.content.read(), 1) if 'description' not in parameters or len(parameters['description']) == 0: extra_info = {} else: extra_info = {'description': parameters['description'][0]} def _on_url_fetched(data): return TorrentDef.load_from_memory(data) def _on_magnet_fetched(meta_info): return TorrentDef.load_from_dict(meta_info) def _on_torrent_def_loaded(torrent_def): if self.is_chant_channel: # We have to get my channel again since we are in a different database session now with db_session: channel = self.session.lm.mds.get_my_channel() channel.add_torrent_to_channel(torrent_def, extra_info) else: channel = self.get_channel_from_db(self.cid) self.session.add_torrent_def_to_channel(channel[0], torrent_def, extra_info, forward=True) return self.path def _on_added(added): request.write(json.dumps({"added": added})) request.finish() def _on_add_failed(failure): failure.trap(ValueError, DuplicateTorrentFileError, SchemeNotSupported) self._logger.exception(failure.value) request.write(BaseChannelsEndpoint.return_500(self, request, failure.value)) request.finish() def _on_timeout(_): request.write(BaseChannelsEndpoint.return_500(self, request, RuntimeError("Metainfo timeout"))) request.finish() if self.path.startswith("http:") or self.path.startswith("https:"): self.deferred = http_get(self.path) self.deferred.addCallback(_on_url_fetched) if self.path.startswith("magnet:"): try: self.session.lm.ltmgr.get_metainfo(self.path, callback=self.deferred.callback, timeout=30, timeout_callback=_on_timeout, notify=True) except Exception as ex: self.deferred.errback(ex) self.deferred.addCallback(_on_magnet_fetched) self.deferred.addCallback(_on_torrent_def_loaded) self.deferred.addCallback(_on_added) self.deferred.addErrback(_on_add_failed) return NOT_DONE_YET