Skip to content
New issue

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

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

Already on GitHub? # to your account

Fixed bot detection on all clients #425

Merged
merged 1 commit into from
Feb 1, 2025
Merged
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
30 changes: 19 additions & 11 deletions pytubefix/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def __init__(
# oauth can only be used by the TV and TV_EMBED client.
self.client = 'TV' if use_oauth else self.client

self.fallback_clients = ['MWEB', 'IOS', 'TV']
self.fallback_clients = ['TV', 'IOS']

self._signature_timestamp: dict = {}
self._visitor_data = None
Expand Down Expand Up @@ -233,16 +233,21 @@ def visitor_data(self) -> str:
if self._visitor_data:
return self._visitor_data

try:
logger.debug("Looking for visitorData in initial_data")
self._visitor_data = extract.visitor_data(str(self.initial_data['responseContext']))
except (KeyError, pytubefix.exceptions.RegexMatchError):
logger.debug("Unable to obtain visitorData from initial_data. Trying to request from the WEB client")
innertube_response = InnerTube('WEB').player(self.video_id)
if InnerTube(self.client).require_po_token:
try:
self._visitor_data = innertube_response['responseContext']['visitorData']
except KeyError:
self._visitor_data = innertube_response['responseContext']['serviceTrackingParams'][0]['params'][6]['value']
logger.debug("Looking for visitorData in initial_data")
self._visitor_data = extract.visitor_data(str(self.initial_data['responseContext']))
logger.debug('VisitorData obtained successfully')
return self._visitor_data
except (KeyError, pytubefix.exceptions.RegexMatchError):
logger.debug("Unable to obtain visitorData from initial_data. Trying to request from the WEB client")

logger.debug("Looking for visitorData in InnerTube API")
innertube_response = InnerTube('WEB').player(self.video_id)
try:
self._visitor_data = innertube_response['responseContext']['visitorData']
except KeyError:
self._visitor_data = innertube_response['responseContext']['serviceTrackingParams'][0]['params'][6]['value']
logger.debug('VisitorData obtained successfully')

return self._visitor_data
Expand Down Expand Up @@ -471,6 +476,9 @@ def call_innertube():
logger.debug(f"The {self.client} client requires poToken to obtain functional streams")
logger.debug("Automatically generating poToken")
innertube.insert_po_token(visitor_data=self.visitor_data, po_token=self.pot)
elif not self.use_po_token:
# from 01/22/2025 all clients must send the visitorData in the API request
innertube.insert_visitor_data(visitor_data=self.visitor_data)

response = innertube.player(self.video_id)

Expand Down Expand Up @@ -536,7 +544,7 @@ def age_check(self):
Originally the WEB client was used, but with the implementation of PoToken we switched to MWEB.
"""

self.client = 'MWEB'
self.client = 'TV'
innertube = InnerTube(
client=self.client,
use_oauth=self.use_oauth,
Expand Down
18 changes: 12 additions & 6 deletions pytubefix/innertube.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@
'clientName': 'WEB',
'osName': 'Windows',
'osVersion': '10.0',
'clientVersion': '2.20240726.00.00',
'clientVersion': '2.20250122.01.00',
'platform': 'DESKTOP'
}
}
},
'header': {
'User-Agent': 'Mozilla/5.0',
'X-Youtube-Client-Name': '1',
'X-Youtube-Client-Version': '2.20240726.00.00'
'X-Youtube-Client-Version': '2.20250122.01.00'
},
'api_key': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8',
'require_js_player': True,
Expand Down Expand Up @@ -143,7 +143,7 @@
},
'api_key': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8',
'require_js_player': True,
'require_po_token': False
'require_po_token': True
},

'WEB_KIDS': {
Expand Down Expand Up @@ -707,14 +707,20 @@ def fetch_bearer_token(self):
self.expires = start_time + response_data['expires_in']
self.cache_tokens()

def insert_po_token(self, visitor_data:str=None, po_token:str=None) -> None:
def insert_visitor_data(self, visitor_data: str) -> None:
"""
Insert visitorData and po_token in the API request
Insert visitorData in the API request
"""
self.innertube_context['context']['client'].update({
"visitorData": self.access_visitorData or visitor_data
"visitorData": visitor_data
})

def insert_po_token(self, visitor_data:str=None, po_token:str=None) -> None:
"""
Insert visitorData and po_token in the API request
"""
self.insert_visitor_data(self.access_visitorData or visitor_data)

self.innertube_context.update({
"serviceIntegrityDimensions": {
"poToken": self.access_po_token or po_token
Expand Down