Skip to content

If not running in main thread, create & set a new event loop. #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,33 @@ Import
```
from TikTokAPI import TikTokAPI
```
Get your keys from Cookie. You can get them from the Applications tab in Chrome developer console.
By default it used hardcoded values which may not work after some time.
The keys to extract are `s_v_web_id` and `tt_webid`
```
cookie = {
"s_v_web_id": "<your_key>",
"tt_webid": "<your_key>"
}
```
Get the most trending Videos on TikTok
```
api = TikTokAPI()
api = TikTokAPI(cookie=cookie)
retval = api.getTrending(count=5)
```
Get a user by name
```
api = TikTokAPI()
api = TikTokAPI(cookie=cookie)
user_obj = api.getUserByName("fcbarcelona")
```
Get videos of a user
```
api = TikTokAPI()
api = TikTokAPI(cookie=cookie)
user_videos = api.getVideosByUserName("fcbarcelona")
```
Get likes of a user
```
api = TikTokAPI()
api = TikTokAPI(cookie=cookie)
user_videos = api.getLikesByUserName("fcbarcelona")
```

Expand Down
16 changes: 14 additions & 2 deletions TikTokAPI/tiktok_browser.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import os
import asyncio
from pyppeteer import launch
from pyppeteer import launcher
from .utils import python_list2_web_list


class TikTokBrowser:

def __init__(self, user_agent):

self.userAgent = user_agent
self.args = [
"--no-sandbox",
Expand Down Expand Up @@ -51,10 +52,19 @@ def __init__(self, user_agent):
self.tiktok_dummy_page = "file://" + os.path.join(parent_folder, "website", "tiktok.html")

def fetch_auth_params(self, url, language='en'):

# If not running in the main thread, it's necessary to create & set a
# new event loop.
try:
asyncio.get_event_loop()
except RuntimeError:
asyncio.set_event_loop(asyncio.new_event_loop())

return asyncio.get_event_loop().run_until_complete(self.async_fetch_auth_params(url, language))

async def async_fetch_auth_params(self, url, language):
browser = await launch(self.options)
pplauncher = launcher.Launcher(self.options)
browser = await pplauncher.launch()
page = await browser.newPage()

await page.evaluateOnNewDocument("""() => {
Expand All @@ -80,5 +90,7 @@ async def async_fetch_auth_params(self, url, language):
return token;
}''')

await page.close()
await browser.close()
await pplauncher.killChrome()
return signature
56 changes: 34 additions & 22 deletions TikTokAPI/tiktokapi.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
import os
import string
import random
import urllib.parse
from .utils import random_key, build_get_url, get_req_json, get_req_content, get_req_text
from .tiktok_browser import TikTokBrowser


class VideoException(Exception):
pass


class TikTokAPI(object):

def __init__(self, language='en', browser_lang="en-US", timezone="Asia/Kolkata", region='IN', cookie=None):
def __init__(self, cookie=None, language='en', browser_lang="en-US", timezone="Asia/Kolkata", region='IN'):
self.base_url = "https://t.tiktok.com/api"
self.user_agent = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:79.0) Gecko/20100101 Firefox/79.0"

if cookie is None:
cookie = {}
self.verifyFp = cookie.get("s_v_web_id", "verify_kjf974fd_y7bupmR0_3uRm_43kF_Awde_8K95qt0GcpBk")
self.tt_webid = cookie.get("tt_webid", "6913027209393473025")

self.headers = {
"User-Agent": self.user_agent,
'Host': 't.tiktok.com',
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:79.0) Gecko/20100101 Firefox/79.0',
'Referer': 'https://www.tiktok.com/',
'Cookie': 'tt_webid_v2={}; tt_webid={}'.format(self.tt_webid, self.tt_webid)
}
self.language = language
self.browser_lang = browser_lang
self.timezone = timezone
self.region = region

if cookie is None:
self.verifyFp = random_key(16)
else:
self.verifyFp = cookie
self.default_params = {
"aid": "1988",
"app_name": "tiktok_web",
Expand Down Expand Up @@ -60,7 +69,7 @@ def __init__(self, language='en', browser_lang="en-US", timezone="Asia/Kolkata",

def send_get_request(self, url, params, extra_headers=None):
url = build_get_url(url, params)
did = str(random.randint(10000, 999999999))
did = ''.join(random.choice(string.digits) for num in range(19))
url = build_get_url(url, {self.did_key: did}, append=True)
signature = self.tiktok_browser.fetch_auth_params(url, language=self.language)
url = build_get_url(url, {self.signature_key: signature}, append=True)
Expand Down Expand Up @@ -95,9 +104,10 @@ def getTrending(self, count=30):
return self.send_get_request(url, params)

def getUserByName(self, user_name):
url = self.base_url + "/user/detail/"
url = "https://t.tiktok.com/node/share/user/@" + user_name
params = {
"uniqueId": user_name
"uniqueId": user_name,
"validUniqueId": user_name,
}
for key, val in self.default_params.items():
params[key] = val
Expand Down Expand Up @@ -126,7 +136,7 @@ def getVideosByUserName(self, user_name, count=30):
for key, val in self.default_params.items():
params[key] = val
return self.send_get_request(url, params)

def getLikesByUserName(self, user_name, count=30):
user_data = self.getUserByName(user_name)
user_obj = user_data["userInfo"]["user"]
Expand Down Expand Up @@ -164,7 +174,7 @@ def getVideosByHashTag(self, hashTag, count=30):
hashTag = hashTag.replace("#", "")
hashTag_obj = self.getHashTag(hashTag)
hashTag_id = hashTag_obj["challengeInfo"]["challenge"]["id"]
url = "https://m.tiktok.com/share/item/list"
url = self.base_url + "/challenge/item_list/"
req_default_params = {
"secUid": "",
"type": "3",
Expand All @@ -174,8 +184,9 @@ def getVideosByHashTag(self, hashTag, count=30):
"recType": ""
}
params = {
"id": str(hashTag_id),
"challengeID": str(hashTag_id),
"count": str(count),
"cursor": "0",
}
for key, val in req_default_params.items():
params[key] = val
Expand All @@ -194,7 +205,7 @@ def getMusic(self, music_id):
return self.send_get_request(url, params)

def getVideosByMusic(self, music_id, count=30):
url = "https://m.tiktok.com/share/item/list"
url = self.base_url + "/music/item_list/"
req_default_params = {
"secUid": "",
"type": "4",
Expand All @@ -204,8 +215,9 @@ def getVideosByMusic(self, music_id, count=30):
"recType": ""
}
params = {
"id": str(music_id),
"musicID": str(music_id),
"count": str(count),
"cursor": "0",
}
for key, val in req_default_params.items():
params[key] = val
Expand All @@ -225,22 +237,22 @@ def getVideoById(self, video_id):

def downloadVideoById(self, video_id, save_path):
video_info = self.getVideoById(video_id)
video_url = video_info["itemInfo"]["itemStruct"]["video"]["downloadAddr"]
headers = {"User-Agent": "okhttp"}
video_data = get_req_content(video_url, params=None, headers=headers)
video_url = video_info["itemInfo"]["itemStruct"]["video"]["playAddr"]
video_data = get_req_content(video_url, params=None, headers=self.headers)
with open(save_path, 'wb') as f:
f.write(video_data)

def downloadVideoByIdNoWatermark(self, video_id, save_path):
video_info = self.getVideoById(video_id)
video_url = video_info["itemInfo"]["itemStruct"]["video"]["downloadAddr"]
headers = {"User-Agent": "okhttp"}
video_data = get_req_text(video_url, params=None, headers=headers)
video_data = get_req_text(video_url, params=None, headers=self.headers)
pos = video_data.find("vid:")
if pos == -1:
raise VideoException("Video without watermark not available in new videos")
video_url_no_wm = "https://api2-16-h2.musical.ly/aweme/v1/play/?video_id={" \
"}&vr_type=0&is_play_url=1&source=PackSourceEnum_PUBLISH&media_type=4" \
.format(video_data[pos+4:pos+36])
headers = {"User-Agent": "okhttp"}
video_data_no_wm = get_req_content(video_url_no_wm, params=None, headers=headers)
.format(video_data[pos + 4:pos + 36])

video_data_no_wm = get_req_content(video_url_no_wm, params=None, headers=self.headers)
with open(save_path, 'wb') as f:
f.write(video_data_no_wm)
3 changes: 3 additions & 0 deletions TikTokAPI/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,19 @@ def build_get_url(base_url, params, append=False):


def get_req_json(url, params=None, headers=None):
headers["Host"] = url.split("/")[2]
r = requests.get(url, params=params, headers=headers)
return json.loads(r.text)


def get_req_content(url, params=None, headers=None):
headers["Host"] = url.split("/")[2]
r = requests.get(url, params=params, headers=headers)
return r.content


def get_req_text(url, params=None, headers=None):
headers["Host"] = url.split("/")[2]
r = requests.get(url, params=params, headers=headers)
return r.text

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
setuptools.setup(
name="PyTikTokAPI",
packages=setuptools.find_packages(),
version="0.0.4",
version="0.0.5",
license='MIT',
author="Avilash Kumar",
author_email="[email protected]",
Expand Down
4 changes: 4 additions & 0 deletions tests/cookie.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"s_v_web_id": "verify_kjf974fd_y7bupmR0_3uRm_43kF_Awde_8K95qt0GcpBk",
"tt_webid": "6913027209393473025"
}
5 changes: 3 additions & 2 deletions tests/test_hashtag.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import argparse
from TikTokAPI import TikTokAPI
from utils import read_json_from_file


def getHashTag(hashTag):
api = TikTokAPI()
api = TikTokAPI(read_json_from_file("cookie.json"))
return api.getHashTag(hashTag)


def getVideosByHashTag(hashTag):
api = TikTokAPI()
api = TikTokAPI(read_json_from_file("cookie.json"))
return api.getVideosByHashTag(hashTag, count=10)


Expand Down
5 changes: 3 additions & 2 deletions tests/test_music.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import argparse
from TikTokAPI import TikTokAPI
from utils import read_json_from_file


def getMusic(music_id):
api = TikTokAPI()
api = TikTokAPI(read_json_from_file("cookie.json"))
return api.getMusic(music_id)


def getVideosByMusic(music_id):
api = TikTokAPI()
api = TikTokAPI(read_json_from_file("cookie.json"))
return api.getVideosByMusic(music_id, count=10)


Expand Down
3 changes: 2 additions & 1 deletion tests/test_trending.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from TikTokAPI import TikTokAPI
from utils import read_json_from_file

api = TikTokAPI()
api = TikTokAPI(read_json_from_file("cookie.json"))
retval = api.getTrending(count=5)
print("Trending Videos")
print(retval)
7 changes: 4 additions & 3 deletions tests/test_user.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import argparse
from TikTokAPI import TikTokAPI
from utils import read_json_from_file


def getUser(user_name):
api = TikTokAPI()
api = TikTokAPI(read_json_from_file("cookie.json"))
return api.getUserByName(user_name)


def getVideosByUserName(user_name):
api = TikTokAPI()
api = TikTokAPI(read_json_from_file("cookie.json"))
return api.getVideosByUserName(user_name, count=1)


def getLikesByUserName(user_name):
api = TikTokAPI()
api = TikTokAPI(read_json_from_file("cookie.json"))
return api.getLikesByUserName(user_name, count=1)


Expand Down
7 changes: 4 additions & 3 deletions tests/test_video.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import argparse
from TikTokAPI import TikTokAPI
from utils import read_json_from_file


def getVideoById(video_id):
api = TikTokAPI()
api = TikTokAPI(read_json_from_file("cookie.json"))
return api.getVideoById(video_id)


def downloadVideoById(video_id):
api = TikTokAPI()
api = TikTokAPI(read_json_from_file("cookie.json"))
api.downloadVideoById(video_id, video_id+".mp4")


def downloadVideoByIdNoWatermark(video_id):
api = TikTokAPI()
api = TikTokAPI(read_json_from_file("cookie.json"))
api.downloadVideoByIdNoWatermark(video_id, video_id+"_no_wm.mp4")


Expand Down
9 changes: 9 additions & 0 deletions tests/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import json


def read_json_from_file(filepath):
with open(filepath, 'r', encoding='utf-8') as json_file:
data = json.load(json_file)
if type(data) is str:
return json.loads(data)
return data