From 75aea8689cdd8340c0987f40ccb540f1a38e04cc Mon Sep 17 00:00:00 2001 From: ctaity Date: Fri, 15 Jul 2022 19:29:24 -0300 Subject: [PATCH] V5.2.2 - added error_code and the response to tiktok exceptions (#916) --- .gitignore | 4 +- CITATION.cff | 4 +- TikTokApi/api/sound.py | 2 +- TikTokApi/exceptions.py | 10 ++- TikTokApi/helpers.py | 2 +- TikTokApi/tiktok.py | 157 ++++++++++++++++++++-------------------- setup.py | 2 +- 7 files changed, 93 insertions(+), 88 deletions(-) diff --git a/.gitignore b/.gitignore index f251d31c..9dad7395 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,6 @@ build MANIFEST src .vscode -.env \ No newline at end of file +.env +/.idea/ +/TikTok-Api.iml diff --git a/CITATION.cff b/CITATION.cff index 6f7b1d49..e8c2fc12 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -5,5 +5,5 @@ authors: orcid: "https://orcid.org/0000-0002-9467-4676" title: "TikTokAPI" url: "https://github.com/davidteather/tiktok-api" -version: 5.2.1 -date-released: 2022-7-7 +version: 5.2.2 +date-released: 2022-7-12 diff --git a/TikTokApi/api/sound.py b/TikTokApi/api/sound.py index db743b11..edd7c2ec 100644 --- a/TikTokApi/api/sound.py +++ b/TikTokApi/api/sound.py @@ -174,7 +174,7 @@ def __extract_from_data(self): def __ensure_valid(self): if self.id == "": - raise SoundRemovedException("This sound has been removed!") + raise SoundRemovedException(0, None, "This sound has been removed!") def __repr__(self): return self.__str__() diff --git a/TikTokApi/exceptions.py b/TikTokApi/exceptions.py index 57911440..c8c33f23 100644 --- a/TikTokApi/exceptions.py +++ b/TikTokApi/exceptions.py @@ -1,8 +1,14 @@ class TikTokException(Exception): """Generic exception that all other TikTok errors are children of.""" - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def __init__(self, error_code, raw_response, message): + self.error_code = error_code + self.raw_response = raw_response + self.message = message + super().__init__(self.message) + + def __str__(self): + return f'{self.error_code} -> {self.message}' class CaptchaException(TikTokException): diff --git a/TikTokApi/helpers.py b/TikTokApi/helpers.py index 60addb42..3b8a5469 100644 --- a/TikTokApi/helpers.py +++ b/TikTokApi/helpers.py @@ -28,7 +28,7 @@ def extract_tag_contents(html): if sigi_json: return sigi_json.group(1) else: - raise CaptchaException( + raise CaptchaException(0, None, "TikTok blocks this request displaying a Captcha \nTip: Consider using a proxy or a custom_verify_fp as method parameters" ) diff --git a/TikTokApi/tiktok.py b/TikTokApi/tiktok.py index e28f7548..c7c45c6f 100644 --- a/TikTokApi/tiktok.py +++ b/TikTokApi/tiktok.py @@ -1,30 +1,27 @@ +import asyncio import json import logging import os -import threading -import asyncio import random import string +import threading import time -from typing import ClassVar, Optional -from urllib import request -from urllib.parse import quote, urlencode +from dataclasses import dataclass +from typing import Optional +from urllib.parse import urlencode import requests + +from .api.comment import Comment +from .api.hashtag import Hashtag +from .api.search import Search from .api.sound import Sound +from .api.trending import Trending from .api.user import User -from .api.search import Search -from .api.hashtag import Hashtag from .api.video import Video -from .api.trending import Trending -from .api.comment import Comment - -from playwright.sync_api import sync_playwright - -from .exceptions import * -from .utilities import LOGGER_NAME, update_messager from .browser_utilities.browser import browser -from dataclasses import dataclass +from .exceptions import * +from .utilities import LOGGER_NAME os.environ["no_proxy"] = "127.0.0.1,localhost" @@ -33,6 +30,47 @@ _thread_lock = threading.Lock() +ERROR_CODES = { + "0": "OK", + "450": "CLIENT_PAGE_ERROR", + "10000": "VERIFY_CODE", + "10101": "SERVER_ERROR_NOT_500", + "10102": "USER_NOT_LOGIN", + "10111": "NET_ERROR", + "10113": "SHARK_SLIDE", + "10114": "SHARK_BLOCK", + "10119": "LIVE_NEED_LOGIN", + "10202": "USER_NOT_EXIST", + "10203": "MUSIC_NOT_EXIST", + "10204": "VIDEO_NOT_EXIST", + "10205": "HASHTAG_NOT_EXIST", + "10208": "EFFECT_NOT_EXIST", + "10209": "HASHTAG_BLACK_LIST", + "10210": "LIVE_NOT_EXIST", + "10211": "HASHTAG_SENSITIVITY_WORD", + "10212": "HASHTAG_UNSHELVE", + "10213": "VIDEO_LOW_AGE_M", + "10214": "VIDEO_LOW_AGE_T", + "10215": "VIDEO_ABNORMAL", + "10216": "VIDEO_PRIVATE_BY_USER", + "10217": "VIDEO_FIRST_REVIEW_UNSHELVE", + "10218": "MUSIC_UNSHELVE", + "10219": "MUSIC_NO_COPYRIGHT", + "10220": "VIDEO_UNSHELVE_BY_MUSIC", + "10221": "USER_BAN", + "10222": "USER_PRIVATE", + "10223": "USER_FTC", + "10224": "GAME_NOT_EXIST", + "10225": "USER_UNIQUE_SENSITIVITY", + "10227": "VIDEO_NEED_RECHECK", + "10228": "VIDEO_RISK", + "10229": "VIDEO_R_MASK", + "10230": "VIDEO_RISK_MASK", + "10231": "VIDEO_GEOFENCE_BLOCK", + "10404": "FYP_VIDEO_LIST_LIMIT", + "undefined": "MEDIA_ERROR", +} + class TikTokApi: _is_context_manager = False @@ -46,17 +84,17 @@ class TikTokApi: logger = logging.getLogger(LOGGER_NAME) def __init__( - self, - logging_level: int = logging.WARNING, - request_delay: Optional[int] = None, - custom_device_id: Optional[str] = None, - generate_static_device_id: Optional[bool] = False, - custom_verify_fp: Optional[str] = None, - use_test_endpoints: Optional[bool] = False, - proxy: Optional[str] = None, - executable_path: Optional[str] = None, - *args, - **kwargs, + self, + logging_level: int = logging.WARNING, + request_delay: Optional[int] = None, + custom_device_id: Optional[str] = None, + generate_static_device_id: Optional[bool] = False, + custom_verify_fp: Optional[str] = None, + use_test_endpoints: Optional[bool] = False, + proxy: Optional[str] = None, + executable_path: Optional[str] = None, + *args, + **kwargs, ): """The TikTokApi class. Used to interact with TikTok. This is a singleton class to prevent issues from arising with playwright @@ -302,8 +340,8 @@ def get_data(self, path, subdomain="m", **kwargs) -> dict: try: parsed_data = r.json() if ( - parsed_data.get("type") == "verify" - or parsed_data.get("verifyConfig", {}).get("type", "") == "verify" + parsed_data.get("type") == "verify" + or parsed_data.get("verifyConfig", {}).get("type", "") == "verify" ): self.logger.error( "Tiktok wants to display a captcha.\nResponse:\n%s\nCookies:\n%s\nURL:\n%s", @@ -311,78 +349,37 @@ def get_data(self, path, subdomain="m", **kwargs) -> dict: self._get_cookies(**kwargs), url, ) - raise CaptchaException( + raise CaptchaException(0, None, "TikTok blocks this request displaying a Captcha \nTip: Consider using a proxy or a custom_verify_fp as method parameters" ) # statusCode from props->pageProps->statusCode thanks @adiantek on #403 - error_codes = { - "0": "OK", - "450": "CLIENT_PAGE_ERROR", - "10000": "VERIFY_CODE", - "10101": "SERVER_ERROR_NOT_500", - "10102": "USER_NOT_LOGIN", - "10111": "NET_ERROR", - "10113": "SHARK_SLIDE", - "10114": "SHARK_BLOCK", - "10119": "LIVE_NEED_LOGIN", - "10202": "USER_NOT_EXIST", - "10203": "MUSIC_NOT_EXIST", - "10204": "VIDEO_NOT_EXIST", - "10205": "HASHTAG_NOT_EXIST", - "10208": "EFFECT_NOT_EXIST", - "10209": "HASHTAG_BLACK_LIST", - "10210": "LIVE_NOT_EXIST", - "10211": "HASHTAG_SENSITIVITY_WORD", - "10212": "HASHTAG_UNSHELVE", - "10213": "VIDEO_LOW_AGE_M", - "10214": "VIDEO_LOW_AGE_T", - "10215": "VIDEO_ABNORMAL", - "10216": "VIDEO_PRIVATE_BY_USER", - "10217": "VIDEO_FIRST_REVIEW_UNSHELVE", - "10218": "MUSIC_UNSHELVE", - "10219": "MUSIC_NO_COPYRIGHT", - "10220": "VIDEO_UNSHELVE_BY_MUSIC", - "10221": "USER_BAN", - "10222": "USER_PRIVATE", - "10223": "USER_FTC", - "10224": "GAME_NOT_EXIST", - "10225": "USER_UNIQUE_SENSITIVITY", - "10227": "VIDEO_NEED_RECHECK", - "10228": "VIDEO_RISK", - "10229": "VIDEO_R_MASK", - "10230": "VIDEO_RISK_MASK", - "10231": "VIDEO_GEOFENCE_BLOCK", - "10404": "FYP_VIDEO_LIST_LIMIT", - "undefined": "MEDIA_ERROR", - } + statusCode = parsed_data.get("statusCode", 0) self.logger.debug(f"TikTok Returned: %s", json) if statusCode == 10201: # Invalid Entity - raise NotFoundException( - "TikTok returned a response indicating the entity is invalid" - ) + raise NotFoundException(10201, r, + "TikTok returned a response indicating the entity is invalid" + ) elif statusCode == 10219: # Not available in this region - raise NotAvailableException("Content not available for this region") + raise NotAvailableException(10219, r, "Content not available for this region") elif statusCode != 0 and statusCode != -1: - raise TikTokException( - error_codes.get( - statusCode, f"TikTok sent an unknown StatusCode of {statusCode}" - ) - ) + raise TikTokException(statusCode, r, + ERROR_CODES.get(statusCode, f"TikTok sent an unknown StatusCode of {statusCode}") + ) return r.json() except ValueError as e: text = r.text self.logger.debug("TikTok response: %s", text) if len(text) == 0: - raise EmptyResponseException( + raise EmptyResponseException(0, None, "Empty response from Tiktok to " + url ) from None else: - raise InvalidJSONException("TikTok sent invalid JSON") from e + raise InvalidJSONException(0, r, "TikTok sent invalid JSON") from e def get_data_no_sig(self, path, subdomain="m", **kwargs) -> dict: processed = self._process_kwargs(kwargs) diff --git a/setup.py b/setup.py index 32834c6a..48308507 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name="TikTokApi", packages=setuptools.find_packages(), - version="5.2.1", + version="5.2.2", license="MIT", description="The Unofficial TikTok API Wrapper in Python 3.", author="David Teather",