feat(tiktok): browser-driven signed listings

This commit is contained in:
CREDO23 2026-07-08 16:37:36 +02:00
parent daa0856c44
commit ad8b73477a
9 changed files with 240 additions and 18 deletions

View file

@ -0,0 +1,25 @@
"""Pulling item structs out of captured item_list / search API responses."""
from __future__ import annotations
from app.proprietary.platforms.tiktok.extraction import items_from_response
def test_reads_item_list_shape():
body = {"itemList": [{"id": "1"}, {"id": "2"}], "hasMore": True}
assert items_from_response(body) == [{"id": "1"}, {"id": "2"}]
def test_reads_search_data_shape():
body = {"data": [{"type": 1, "item": {"id": "9"}}, {"type": 4, "item": {}}]}
assert items_from_response(body) == [{"id": "9"}, {}]
def test_skips_malformed_entries():
body = {"data": [{"type": 1}, "junk", {"item": {"id": "7"}}]}
assert items_from_response(body) == [{"id": "7"}]
def test_returns_empty_for_unrelated_json():
assert items_from_response({"statusCode": 0}) == []
assert items_from_response("nope") == []

View file

@ -71,3 +71,27 @@ async def test_scrape_skips_unrecognized_urls():
TikTokScrapeInput(postURLs=["https://example.com/x"]), fetch=fake_fetch
)
assert items == []
async def test_scrape_profile_returns_listing_items():
async def fake_listing(_url: str, _count: int) -> list[dict]:
return [
{"id": "1", "author": {"uniqueId": "a"}},
{"id": "2", "author": {"uniqueId": "a"}},
]
items = await scrape_tiktok(
TikTokScrapeInput(profiles=["a"], resultsPerPage=5), fetch_listing=fake_listing
)
assert [i["id"] for i in items] == ["1", "2"]
assert items[0]["webVideoUrl"] == "https://www.tiktok.com/@a/video/1"
async def test_listing_dedupes_then_caps_per_target():
async def fake_listing(_url: str, _count: int) -> list[dict]:
return [{"id": "1"}, {"id": "1"}, {"id": "2"}, {"id": "3"}]
items = await scrape_tiktok(
TikTokScrapeInput(hashtags=["x"], resultsPerPage=2), fetch_listing=fake_listing
)
assert [i["id"] for i in items] == ["1", "2"]