Skip to content

Commit 4f2c13f

Browse files
davidteatherdronavallipranave-meyerErVijayRaghuwanshigiancarlopro
authored
🎉 V6.2.0 - Various Fixes (#1089)
* GitHub actions failing bc TikTok seems to be detecting the IPs of the GitHub actions 💀 (or ms_tokens are now tied to a specific ip) tests pass locally * changed docs to address bug reports (#1052) * Update video_example.py class name (#1056) I was looking at the examples and noticed that the name of the function in video_example.py is the same as in user_example.py, which led me to believe it was a mistake. I just changed it for better understanding. * Update tiktok.py (#1070) proxy support fix * feat: add comments replies retrieval (#1072) * Correctly filling in emptyresponseexception constructor parameters (#1073) * Update tiktok.py (#1080) Resolves #1074 * Correct file name (#1082) * Update id and keys for video info scraping (Issue fix #1064) (#1086) * Update id and keys for video info scraping * Added error handling for the case such as no video * V6.2.0 * bump version --------- Co-authored-by: Pranav Dronavalli <62522813+dronavallipranav@users.noreply.github.com> Co-authored-by: Edmundo Meyer <80543721+e-meyer@users.noreply.github.com> Co-authored-by: Vijay Raghuwanshi <37011729+ErVijayRaghuwanshi@users.noreply.github.com> Co-authored-by: Giancarlo Rocha <giancarloiff@gmail.com> Co-authored-by: Ben Steel <bendavidsteel@gmail.com> Co-authored-by: Glenn Jocher <glenn.jocher@ultralytics.com> Co-authored-by: Marco <80593920+Marco2929@users.noreply.github.com> Co-authored-by: kenki931128 <kenki.nkmr@gmail.com>
1 parent fdde798 commit 4f2c13f

File tree

9 files changed

+102
-24
lines changed

9 files changed

+102
-24
lines changed

.sphinx/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
project = "TikTokAPI"
1717
copyright = "2023, David Teather"
1818
author = "David Teather"
19-
release = "v6.1.1"
19+
release = "v6.2.0"
2020

2121
# -- General configuration ---------------------------------------------------
2222
# https://www.sphinx-doc.org/en/main/usage/configuration.html#general-configuration

CITATION.cff

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ authors:
55
orcid: "https://orcid.org/0000-0002-9467-4676"
66
title: "TikTokAPI"
77
url: "https://github.com/davidteather/tiktok-api"
8-
version: 6.1.1
9-
date-released: 2023-8-20
8+
version: 6.2.0
9+
date-released: 2023-11-27

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ Please don't open an issue if you're experiencing one of these just comment if t
8585

8686
* **Browser Has no Attribute** - make sure you ran `python3 -m playwright install`, if your error persists try the [playwright-python](https://github.com/microsoft/playwright-python) quickstart guide and diagnose issues from there.
8787

88+
* **API methods returning Coroutine** - many of the API's methods are async so make sure your program awaits them for proper functionality
89+
8890
## Quick Start Guide
8991

9092
Here's a quick bit of code to get the most recent trending videos on TikTok. There's more examples in the [examples](https://github.com/davidteather/TikTok-Api/tree/main/examples) directory.
@@ -111,7 +113,7 @@ if __name__ == "__main__":
111113

112114
To directly run the example scripts from the repository root, use the `-m` option on python.
113115
```sh
114-
python -m examples.get_trending
116+
python -m examples.trending_example
115117
```
116118

117119
You can access the full data dictionary the object was created from with `.as_dict`. On a video this may look like

TikTokApi/api/comment.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from __future__ import annotations
22

3-
from typing import ClassVar, Optional
3+
from typing import ClassVar, Iterator, Optional
44
from typing import TYPE_CHECKING, ClassVar, Optional
55

6+
from TikTokApi.exceptions import InvalidResponseException
7+
68
if TYPE_CHECKING:
79
from ..tiktok import TikTokApi
810
from .user import User
@@ -49,6 +51,38 @@ def __extract_from_data(self):
4951
)
5052
self.likes_count = self.as_dict["digg_count"]
5153

54+
async def replies(self, count=20, cursor=0, **kwargs) -> Iterator[Comment]:
55+
found = 0
56+
57+
while found < count:
58+
params = {
59+
"count": 20,
60+
"cursor": cursor,
61+
"item_id": self.author.user_id,
62+
"comment_id": self.id,
63+
}
64+
65+
resp = await self.parent.make_request(
66+
url="https://www.tiktok.com/api/comment/list/reply/",
67+
params=params,
68+
headers=kwargs.get("headers"),
69+
session_index=kwargs.get("session_index"),
70+
)
71+
72+
if resp is None:
73+
raise InvalidResponseException(
74+
resp, "TikTok returned an invalid response."
75+
)
76+
77+
for comment in resp.get("comments", []):
78+
yield self.parent.comment(data=comment)
79+
found += 1
80+
81+
if not resp.get("has_more", False):
82+
return
83+
84+
cursor = resp.get("cursor")
85+
5286
def __repr__(self):
5387
return self.__str__()
5488

TikTokApi/api/video.py

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -102,25 +102,55 @@ async def info(self, **kwargs) -> dict:
102102
r.text, "TikTok returned an invalid response.", error_code=r.status_code
103103
)
104104

105+
# Try SIGI_STATE first
105106
# extract tag <script id="SIGI_STATE" type="application/json">{..}</script>
106107
# extract json in the middle
107108

108109
start = r.text.find('<script id="SIGI_STATE" type="application/json">')
109-
if start == -1:
110-
raise InvalidResponseException(
111-
r.text, "TikTok returned an invalid response.", error_code=r.status_code
112-
)
110+
if start != -1:
111+
start += len('<script id="SIGI_STATE" type="application/json">')
112+
end = r.text.find("</script>", start)
113113

114-
start += len('<script id="SIGI_STATE" type="application/json">')
115-
end = r.text.find("</script>", start)
114+
if end == -1:
115+
raise InvalidResponseException(
116+
r.text, "TikTok returned an invalid response.", error_code=r.status_code
117+
)
116118

117-
if end == -1:
118-
raise InvalidResponseException(
119-
r.text, "TikTok returned an invalid response.", error_code=r.status_code
120-
)
119+
data = json.loads(r.text[start:end])
120+
video_info = data["ItemModule"][self.id]
121+
else:
122+
# Try __UNIVERSAL_DATA_FOR_REHYDRATION__ next
121123

122-
data = json.loads(r.text[start:end])
123-
video_info = data["ItemModule"][self.id]
124+
# extract tag <script id="__UNIVERSAL_DATA_FOR_REHYDRATION__" type="application/json">{..}</script>
125+
# extract json in the middle
126+
127+
start = r.text.find('<script id="__UNIVERSAL_DATA_FOR_REHYDRATION__" type="application/json">')
128+
if start == -1:
129+
raise InvalidResponseException(
130+
r.text, "TikTok returned an invalid response.", error_code=r.status_code
131+
)
132+
133+
start += len('<script id="__UNIVERSAL_DATA_FOR_REHYDRATION__" type="application/json">')
134+
end = r.text.find("</script>", start)
135+
136+
if end == -1:
137+
raise InvalidResponseException(
138+
r.text, "TikTok returned an invalid response.", error_code=r.status_code
139+
)
140+
141+
data = json.loads(r.text[start:end])
142+
default_scope = data.get("__DEFAULT_SCOPE__", {})
143+
video_detail = default_scope.get("webapp.video-detail", {})
144+
if video_detail.get("statusCode", 0) != 0: # assume 0 if not present
145+
raise InvalidResponseException(
146+
r.text, "TikTok returned an invalid response structure.", error_code=r.status_code
147+
)
148+
video_info = video_detail.get("itemInfo", {}).get("itemStruct")
149+
if video_info is None:
150+
raise InvalidResponseException(
151+
r.text, "TikTok returned an invalid response structure.", error_code=r.status_code
152+
)
153+
124154
self.as_dict = video_info
125155
self.__extract_from_data()
126156
return video_info

TikTokApi/tiktok.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ async def create_sessions(
243243
override_browser_args = ["--headless=new"]
244244
headless = False # managed by the arg
245245
self.browser = await self.playwright.chromium.launch(
246-
headless=headless, args=override_browser_args
246+
headless=headless, args=override_browser_args, proxy=random_choice(proxies)
247247
)
248248

249249
await asyncio.gather(
@@ -338,6 +338,7 @@ async def run_fetch_script(self, url: str, headers: dict, **kwargs):
338338
async def generate_x_bogus(self, url: str, **kwargs):
339339
"""Generate the X-Bogus header for a url"""
340340
_, session = self._get_session(**kwargs)
341+
await session.page.wait_for_function("window.byted_acrawler !== undefined")
341342
result = await session.page.evaluate(
342343
f'() => {{ return window.byted_acrawler.frontierSign("{url}") }}'
343344
)
@@ -426,7 +427,7 @@ async def make_request(
426427
raise Exception("TikTokApi.run_fetch_script returned None")
427428

428429
if result == "":
429-
raise EmptyResponseException()
430+
raise EmptyResponseException(result, "TikTok returned an empty response")
430431

431432
try:
432433
data = json.loads(result)

examples/video_example.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
) # set your own ms_token, think it might need to have visited a profile
88

99

10-
async def user_example():
10+
async def get_video_example():
1111
async with TikTokApi() as api:
1212
await api.create_sessions(ms_tokens=[ms_token], num_sessions=1, sleep_after=3)
1313
video = api.video(
@@ -23,4 +23,4 @@ async def user_example():
2323

2424

2525
if __name__ == "__main__":
26-
asyncio.run(user_example())
26+
asyncio.run(get_video_example())

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
setuptools.setup(
99
name="TikTokApi",
1010
packages=setuptools.find_packages(),
11-
version="6.1.1",
11+
version="6.2.0",
1212
license="MIT",
1313
description="The Unofficial TikTok API Wrapper in Python 3.",
1414
author="David Teather",

tests/test_search.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,20 @@
44

55
ms_token = os.environ.get("ms_token", None)
66

7-
87
@pytest.mark.asyncio
9-
async def test_users():
8+
async def test_users_single_page():
9+
api = TikTokApi()
10+
async with api:
11+
await api.create_sessions(ms_tokens=[ms_token], num_sessions=1, sleep_after=3)
12+
count = 0
13+
async for user in api.search.users("therock", count=10):
14+
count += 1
15+
16+
assert count >= 10
17+
18+
#@pytest.mark.asyncio
19+
@pytest.mark.skip(reason="Known issue, see #1088 (https://github.com/davidteather/TikTok-Api/issues/1088)")
20+
async def test_users_multi_page():
1021
api = TikTokApi()
1122
async with api:
1223
await api.create_sessions(ms_tokens=[ms_token], num_sessions=1, sleep_after=3)

0 commit comments

Comments
 (0)