async def post_commit(self, request): channel_pk, channel_id = self.get_channel_from_request(request) with db_session: if channel_id == 0: for t in self.session.mds.CollectionNode.commit_all_channels(): self.session.gigachannel_manager.updated_my_channel(TorrentDef.load_from_dict(t)) else: coll = self.session.mds.CollectionNode.get(public_key=database_blob(channel_pk), id_=channel_id) if not coll: return RESTResponse({"success": False}, status=HTTP_NOT_FOUND) torrent_dict = coll.commit_channel_torrent() if torrent_dict: self.session.gigachannel_manager.updated_my_channel(TorrentDef.load_from_dict(torrent_dict)) return RESTResponse({"success": True})
def update_trackers(self, infohash, trackers): """ Update the trackers for a download. :param infohash: infohash of the torrent that needs to be updated :param trackers: A list of tracker urls. """ download = self.get_download(infohash) if download: old_def = download.get_def() old_trackers = old_def.get_trackers_as_single_tuple() new_trackers = list(set(trackers) - set(old_trackers)) all_trackers = list(old_trackers) + new_trackers if new_trackers: # Add new trackers to the download download.add_trackers(new_trackers) # Create a new TorrentDef if isinstance(old_def, TorrentDefNoMetainfo): new_def = TorrentDefNoMetainfo(old_def.get_infohash(), old_def.get_name(), download.get_magnet_link()) else: metainfo = old_def.get_metainfo() if len(all_trackers) > 1: metainfo["announce-list"] = [all_trackers] else: metainfo["announce"] = all_trackers[0] new_def = TorrentDef.load_from_dict(metainfo) # Set TorrentDef + checkpoint download.set_def(new_def) download.checkpoint()
def on_metadata_received_alert(self, _): torrent_info = get_info_from_handle(self.handle) if not torrent_info: return metadata = { b'info': bdecode_compat(torrent_info.metadata()), b'leechers': 0, b'seeders': 0 } trackers = [ tracker['url'].encode('utf-8') for tracker in self.handle.trackers() ] if len(trackers) > 1: metadata[b"announce-list"] = [trackers] elif trackers: metadata[b"announce"] = trackers[0] for peer in self.handle.get_peer_info(): if peer.progress == 1: metadata[b"seeders"] += 1 else: metadata[b"leechers"] += 1 try: self.tdef = TorrentDef.load_from_dict(metadata) except ValueError as ve: self._logger.exception(ve) return self.set_selected_files() self.checkpoint()
def convert_config_to_tribler75(state_dir): """ Convert the download config files from Tribler 7.4 to 7.5 format. """ for filename in (state_dir / STATEDIR_CHECKPOINT_DIR).glob('*.conf'): try: config = DownloadConfig.load(filename) # Convert resume data resumedata = config.get_engineresumedata() if b'mapped_files' in resumedata: resumedata.pop(b'mapped_files') config.set_engineresumedata(resumedata) config.write(str(filename)) # Convert metainfo metainfo = config.get_metainfo() if not config.config['download_defaults'].get( 'selected_files') or not metainfo: continue # no conversion needed/possible, selected files will be reset to their default (i.e., all files) tdef = TorrentDef.load_from_dict(metainfo) config.set_selected_files([ tdef.get_index_of_file_in_files(fn) for fn in config.config['download_defaults'].pop('selected_files') ]) config.write(str(filename)) except ConfigObjParseError: logger.error("Could not parse %s file so removing it", filename) os.remove(filename)
def test_updated_my_channel(enable_chant, personal_channel, channel_manager, mock_dlmgr, session, tmpdir): tdef = TorrentDef.load_from_dict(update_metainfo) session.dlmgr.start_download = Mock() session.dlmgr.download_exists = lambda *_: False session.mds.channels_dir = "bla" session.config.get_state_dir = lambda: Path(tmpdir / "foo") channel_manager.updated_my_channel(tdef) session.dlmgr.start_download.assert_called_once()
def load_checkpoint(self, filename): try: config = DownloadConfig.load(filename) except Exception: self._logger.exception("Could not open checkpoint file %s", filename) return metainfo = config.get_metainfo() if not metainfo: self._logger.error( "Could not resume checkpoint %s; metainfo not found", filename) return if not isinstance(metainfo, dict): self._logger.error( "Could not resume checkpoint %s; metainfo is not dict %s %s", filename, type(metainfo), repr(metainfo)) return try: url = metainfo.get(b'url', None) url = url.decode('utf-8') if url else url tdef = (TorrentDefNoMetainfo(metainfo[b'infohash'], metainfo[b'name'], url) if b'infohash' in metainfo else TorrentDef.load_from_dict(metainfo)) except (KeyError, ValueError) as e: self._logger.exception( "Could not restore tdef from metainfo dict: %s %s ", e, metainfo) return if config.get_bootstrap_download(): if hexlify(tdef.get_infohash( )) != self.tribler_session.config.get_bootstrap_infohash(): self.remove_config(tdef.get_infohash()) return config.state_dir = self.tribler_session.config.get_state_dir() if config.get_dest_dir() == '': # removed torrent ignoring self._logger.info("Removing checkpoint %s destdir is %s", filename, config.get_dest_dir()) os.remove(filename) return try: if self.download_exists(tdef.get_infohash()): self._logger.info( "Not resuming checkpoint because download has already been added" ) else: self.start_download(tdef=tdef, config=config) except Exception: self._logger.exception( "Not resume checkpoint due to exception while adding download")
def test_updated_my_channel(self): with db_session: chan = self.generate_personal_channel() metainfo = chan.commit_channel_torrent() tdef = TorrentDef.load_from_dict(metainfo) self.mock_session.dlmgr.start_download = Mock() self.mock_session.dlmgr.download_exists = lambda *_: False self.mock_session.mds.channels_dir = "bla" self.mock_session.config.get_state_dir = lambda: "foo" self.chanman.updated_my_channel(tdef) self.mock_session.dlmgr.start_download.assert_called_once()
async def start_download_from_uri(self, uri, config=None): if uri.startswith("http"): tdef = await TorrentDef.load_from_url(uri) return self.start_download(tdef=tdef, config=config) if uri.startswith("magnet:"): name, infohash, _ = parse_magnetlink(uri) if infohash is None: raise RuntimeError("Missing infohash") if infohash in self.metainfo_cache: tdef = TorrentDef.load_from_dict(self.metainfo_cache[infohash]['meta_info']) else: tdef = TorrentDefNoMetainfo(infohash, "Unknown name" if name is None else name, url=uri) return self.start_download(tdef=tdef, config=config) if uri.startswith("file:"): argument = url2pathname(uri[5:]) return self.start_download(torrent_file=argument, config=config) raise Exception("invalid uri")
async def regenerate_channel_torrent(self, channel_pk, channel_id): self._logger.info("Regenerating personal channel %s %i", hexlify(channel_pk), channel_id) with db_session: channel = self.session.mds.ChannelMetadata.get( public_key=channel_pk, id_=channel_id) if channel is None: self._logger.warning( "Tried to regenerate non-existing channel %s %i", hexlify(channel_pk), channel_id) return None for d in self.session.dlmgr.get_downloads_by_name(channel.dirname): await self.session.dlmgr.remove_download(d, remove_content=True) regenerated = channel.consolidate_channel_torrent() # If the user created their channel, but added no torrents to it, # the channel torrent will not be created. if regenerated is None: return None tdef = TorrentDef.load_from_dict(regenerated) self.updated_my_channel(tdef) return tdef
async def get_torrent_info(self, request): args = request.query hops = None if 'hops' in args: try: hops = int(args['hops']) except ValueError: return RESTResponse( { "error": f"wrong value of 'hops' parameter: {repr(args['hops'])}" }, status=HTTP_BAD_REQUEST) if 'uri' not in args or not args['uri']: return RESTResponse({"error": "uri parameter missing"}, status=HTTP_BAD_REQUEST) uri = args['uri'] if uri.startswith('file:'): try: filename = url2pathname(uri[5:]) tdef = TorrentDef.load(filename) metainfo = tdef.get_metainfo() except (TypeError, RuntimeError): return RESTResponse( {"error": "error while decoding torrent file"}, status=HTTP_INTERNAL_SERVER_ERROR) elif uri.startswith('http'): try: async with ClientSession(raise_for_status=True) as session: response = await session.get(uri) response = await response.read() except (ServerConnectionError, ClientResponseError) as e: return RESTResponse({"error": str(e)}, status=HTTP_INTERNAL_SERVER_ERROR) if response.startswith(b'magnet'): _, infohash, _ = parse_magnetlink(response) if infohash: metainfo = await self.session.dlmgr.get_metainfo( infohash, timeout=60, hops=hops, url=response) else: metainfo = bdecode_compat(response) elif uri.startswith('magnet'): infohash = parse_magnetlink(uri)[1] if infohash is None: return RESTResponse({"error": "missing infohash"}, status=HTTP_BAD_REQUEST) metainfo = await self.session.dlmgr.get_metainfo(infohash, timeout=60, hops=hops, url=uri) else: return RESTResponse({"error": "invalid uri"}, status=HTTP_BAD_REQUEST) if not metainfo: return RESTResponse({"error": "metainfo error"}, status=HTTP_INTERNAL_SERVER_ERROR) if not isinstance(metainfo, dict) or b'info' not in metainfo: self._logger.warning("Received metainfo is not a valid dictionary") return RESTResponse({"error": "invalid response"}, status=HTTP_INTERNAL_SERVER_ERROR) # Add the torrent to GigaChannel as a free-for-all entry, so others can search it self.session.mds.TorrentMetadata.add_ffa_from_dict( tdef_to_metadata_dict(TorrentDef.load_from_dict(metainfo))) # TODO(Martijn): store the stuff in a database!!! # TODO(Vadim): this means cache the downloaded torrent in a binary storage, like LevelDB infohash = hashlib.sha1(bencode(metainfo[b'info'])).digest() download = self.session.dlmgr.downloads.get(infohash) metainfo_request = self.session.dlmgr.metainfo_requests.get( infohash, [None])[0] download_is_metainfo_request = download == metainfo_request # Check if the torrent is already in the downloads encoded_metainfo = deepcopy(metainfo) # FIXME: json.dumps garbles binary data that is used by the 'pieces' field # However, this is fine as long as the GUI does not use this field. encoded_metainfo[b'info'][b'pieces'] = hexlify( encoded_metainfo[b'info'][b'pieces']).encode('utf-8') encoded_metainfo = hexlify( json.dumps(recursive_unicode(encoded_metainfo, ignore_errors=True), ensure_ascii=False).encode('utf-8')) return RESTResponse({ "metainfo": encoded_metainfo, "download_exists": download and not download_is_metainfo_request })
def commit(self): _logger.info('Commit changes') for t in self.community.mds.CollectionNode.commit_all_channels(): self.manager.updated_my_channel(TorrentDef.load_from_dict(t))
async def add_torrent_to_channel(self, request): channel_pk, channel_id = self.get_channel_from_request(request) with db_session: channel = self.session.mds.CollectionNode.get( public_key=database_blob(channel_pk), id_=channel_id) if not channel: return RESTResponse({"error": "Unknown channel"}, status=HTTP_NOT_FOUND) parameters = await request.json() extra_info = {} if parameters.get('description', None): extra_info = {'description': parameters['description']} # First, check whether we did upload a magnet link or URL if parameters.get('uri', None): uri = parameters['uri'] if uri.startswith("http:") or uri.startswith("https:"): async with ClientSession() as session: response = await session.get(uri) data = await response.read() tdef = TorrentDef.load_from_memory(data) elif uri.startswith("magnet:"): _, xt, _ = parse_magnetlink(uri) if (xt and is_infohash(codecs.encode(xt, 'hex')) and (self.session.mds.torrent_exists_in_personal_channel(xt) or channel.copy_torrent_from_infohash(xt))): return RESTResponse({"added": 1}) meta_info = await self.session.dlmgr.get_metainfo(xt, timeout=30, url=uri) if not meta_info: raise RuntimeError("Metainfo timeout") tdef = TorrentDef.load_from_dict(meta_info) else: return RESTResponse({"error": "unknown uri type"}, status=HTTP_BAD_REQUEST) added = 0 if tdef: channel.add_torrent_to_channel(tdef, extra_info) added = 1 return RESTResponse({"added": added}) torrents_dir = None if parameters.get('torrents_dir', None): torrents_dir = parameters['torrents_dir'] if not path_util.isabs(torrents_dir): return RESTResponse( {"error": "the torrents_dir should point to a directory"}, status=HTTP_BAD_REQUEST) recursive = False if parameters.get('recursive'): recursive = parameters['recursive'] if not torrents_dir: return RESTResponse( { "error": "the torrents_dir parameter should be provided when the recursive parameter is set" }, status=HTTP_BAD_REQUEST, ) if torrents_dir: torrents_list, errors_list = channel.add_torrents_from_dir( torrents_dir, recursive) return RESTResponse({ "added": len(torrents_list), "errors": errors_list }) if not parameters.get('torrent', None): return RESTResponse({"error": "torrent parameter missing"}, status=HTTP_BAD_REQUEST) # Try to parse the torrent data # Any errors will be handled by the error_middleware torrent = base64.b64decode(parameters['torrent']) torrent_def = TorrentDef.load_from_memory(torrent) channel.add_torrent_to_channel(torrent_def, extra_info) return RESTResponse({"added": 1})
def test_load_from_dict(): with open(TESTS_DATA_DIR / "bak_single.torrent", mode='rb') as torrent_file: encoded_metainfo = torrent_file.read() assert TorrentDef.load_from_dict(bdecode_compat(encoded_metainfo))