Exemplo n.º 1
0
    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})
Exemplo n.º 2
0
    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()
Exemplo n.º 3
0
    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()
Exemplo n.º 4
0
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)
Exemplo n.º 5
0
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()
Exemplo n.º 6
0
    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")
Exemplo n.º 7
0
 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()
Exemplo n.º 8
0
 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")
Exemplo n.º 9
0
 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
Exemplo n.º 10
0
    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
        })
Exemplo n.º 11
0
    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))
Exemplo n.º 12
0
    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})
Exemplo n.º 13
0
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))