diff --git a/src/tribler/core/libtorrent/restapi/downloads_endpoint.py b/src/tribler/core/libtorrent/restapi/downloads_endpoint.py index 2d4c0f33f0..c1a350aa2b 100644 --- a/src/tribler/core/libtorrent/restapi/downloads_endpoint.py +++ b/src/tribler/core/libtorrent/restapi/downloads_endpoint.py @@ -702,6 +702,17 @@ async def remove_tracker(self, request: Request) -> RESTResponse: "message": str(e) }}, status=HTTP_INTERNAL_SERVER_ERROR) + if download.tdef.metainfo: + url_bytes = url.encode() + if download.tdef.metainfo.get(b'announce-list'): + download.tdef.metainfo[b'announce-list'] = [e for e in download.tdef.metainfo[b'announce-list'] + if e[0] != url_bytes] + if url_bytes == download.tdef.metainfo.get(b"announce"): + if download.tdef.metainfo.get(b'announce-list'): + download.tdef.metainfo[b"announce"] = download.tdef.metainfo[b'announce-list'][0][0] + else: + download.tdef.metainfo.pop(b"announce") + return RESTResponse({"removed": True}) @docs( diff --git a/src/tribler/test_unit/core/libtorrent/restapi/test_downloads_endpoint.py b/src/tribler/test_unit/core/libtorrent/restapi/test_downloads_endpoint.py index c97df4232c..349f1f4ab2 100644 --- a/src/tribler/test_unit/core/libtorrent/restapi/test_downloads_endpoint.py +++ b/src/tribler/test_unit/core/libtorrent/restapi/test_downloads_endpoint.py @@ -1199,6 +1199,93 @@ async def test_remove_tracker(self) -> None: self.assertTrue(response_body_json["removed"]) self.assertListEqual([{"url": "http://127.0.0.1/somethingelse", "verified": True}], trackers) + async def test_remove_tracker_from_metainfo_announce(self) -> None: + """ + Test if trackers can be removed from a download's metainfo with only b"announce". + """ + download = self.create_mock_download() + download.tdef = TorrentDef.load_from_memory(TORRENT_WITH_VIDEO) + download.tdef.metainfo[b"announce"] = b"http://127.0.0.1/announce" + download.handle = Mock(is_valid=Mock(return_value=True), trackers=Mock(return_value=[])) + self.download_manager.get_download = Mock(return_value=download) + url = "http://127.0.0.1/announce" + + response = await self.endpoint.remove_tracker( + GenericTrackerRequest(hexlify(download.tdef.infohash).decode(), url, "DELETE", "trackers") + ) + response_body_json = await response_to_json(response) + + self.assertEqual(200, response.status) + self.assertTrue(response_body_json["removed"]) + self.assertNotIn(b"announce", download.tdef.metainfo) + + async def test_remove_tracker_from_metainfo_announce_list(self) -> None: + """ + Test if trackers can be removed from a download's metainfo with only b"announce-list". + """ + download = self.create_mock_download() + download.tdef = TorrentDef.load_from_memory(TORRENT_WITH_VIDEO) + download.tdef.metainfo[b"announce-list"] = [[b"http://127.0.0.1/somethingelse"], [b"http://127.0.0.1/announce"]] + download.handle = Mock(is_valid=Mock(return_value=True), trackers=Mock(return_value=[])) + self.download_manager.get_download = Mock(return_value=download) + url = "http://127.0.0.1/announce" + + response = await self.endpoint.remove_tracker( + GenericTrackerRequest(hexlify(download.tdef.infohash).decode(), url, "DELETE", "trackers") + ) + response_body_json = await response_to_json(response) + + self.assertEqual(200, response.status) + self.assertTrue(response_body_json["removed"]) + self.assertEqual(1, len(download.tdef.metainfo[b"announce-list"])) + self.assertEqual(b"http://127.0.0.1/somethingelse", download.tdef.metainfo[b"announce-list"][0][0]) + + async def test_remove_tracker_from_metainfo_announce_both_first(self) -> None: + """ + Test if trackers can be removed from a download's metainfo where the b"announce" is first in b"announce-list". + """ + download = self.create_mock_download() + download.tdef = TorrentDef.load_from_memory(TORRENT_WITH_VIDEO) + download.tdef.metainfo[b"announce"] = b"http://127.0.0.1/announce" + download.tdef.metainfo[b"announce-list"] = [[b"http://127.0.0.1/announce"], [b"http://127.0.0.1/somethingelse"]] + download.handle = Mock(is_valid=Mock(return_value=True), trackers=Mock(return_value=[])) + self.download_manager.get_download = Mock(return_value=download) + url = "http://127.0.0.1/announce" + + response = await self.endpoint.remove_tracker( + GenericTrackerRequest(hexlify(download.tdef.infohash).decode(), url, "DELETE", "trackers") + ) + response_body_json = await response_to_json(response) + + self.assertEqual(200, response.status) + self.assertTrue(response_body_json["removed"]) + self.assertEqual(1, len(download.tdef.metainfo[b"announce-list"])) + self.assertEqual(b"http://127.0.0.1/somethingelse", download.tdef.metainfo[b"announce-list"][0][0]) + self.assertEqual(b"http://127.0.0.1/somethingelse", download.tdef.metainfo[b"announce"]) + + async def test_remove_tracker_from_metainfo_announce_both_second(self) -> None: + """ + Test if trackers can be removed from a download's metainfo where the b"announce" is second in b"announce-list". + """ + download = self.create_mock_download() + download.tdef = TorrentDef.load_from_memory(TORRENT_WITH_VIDEO) + download.tdef.metainfo[b"announce"] = b"http://127.0.0.1/announce" + download.tdef.metainfo[b"announce-list"] = [[b"http://127.0.0.1/somethingelse"], [b"http://127.0.0.1/announce"]] + download.handle = Mock(is_valid=Mock(return_value=True), trackers=Mock(return_value=[])) + self.download_manager.get_download = Mock(return_value=download) + url = "http://127.0.0.1/announce" + + response = await self.endpoint.remove_tracker( + GenericTrackerRequest(hexlify(download.tdef.infohash).decode(), url, "DELETE", "trackers") + ) + response_body_json = await response_to_json(response) + + self.assertEqual(200, response.status) + self.assertTrue(response_body_json["removed"]) + self.assertEqual(1, len(download.tdef.metainfo[b"announce-list"])) + self.assertEqual(b"http://127.0.0.1/somethingelse", download.tdef.metainfo[b"announce-list"][0][0]) + self.assertEqual(b"http://127.0.0.1/somethingelse", download.tdef.metainfo[b"announce"]) + async def test_remove_tracker_no_download(self) -> None: """ Test if removing a tracker fails when no download is found.