Skip to content
This repository was archived by the owner on Aug 17, 2025. It is now read-only.

Commit b27513a

Browse files
committed
- Bypass bot verification
- Impersonate Chrome browser - Resolves #2
1 parent 009afba commit b27513a

10 files changed

Lines changed: 142 additions & 31 deletions

File tree

Docs/CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,10 @@
5454

5555
# v1.0.4
5656
**What's new?**
57-
- Bug fixed - **resume download**
57+
- Bug fixed - **resume download**
58+
59+
# v1.2.0
60+
**What's new?**
61+
- Bypass bot verification
62+
- Impersonate Chrome browser
63+
- Resolves #2

README.md

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,54 @@ cd y2mate-api
6262
pip install .
6363
```
6464

65-
For Windows users you can as well download the executables from [here](https://github.com/Simatwa/y2mate-api/releases/download/v1.0.2/main.exe)
66-
6765
# Usage
6866

6967
`$ y2mate -f <mp3/mp4> <youtube-link or video id or keyword>`
7068

69+
Following the introduction of Cloudflare protection on the host, y2mate.com, you have to authenticate you're a human right from your **Chrome Browser** and then proceed to extract `cf_clearance` cookie value, which will in turn be passed over to the script.
70+
71+
### `cf_clearance` extraction process.
72+
73+
i. Install **Http Tracker** Chrome extension using [this link](https://chromewebstore.google.com/detail/http-tracker/fklakbbaaknbgcedidhblbnhclijnhbi).
74+
i. Using **Chrome Browswer**, navigate to y2mate.com and pass the bot verification stage.
75+
ii. Start the *Http Tracker* extension.
76+
iii. On the search section of y2mate.com, key-in anything and press <kbd>enter</kbd>
77+
iv. Return to the Http Tracker window and look for any of the recent urls containing `*y2mate.com*` and click it.
78+
v. Navigate down to the coookies section and copy the value of key `cf_clearance`. In the proceeding guides I will be referring to this value as **CF-CLEARANCE**
79+
80+
![CF-CLEARANCE highlighted](https://github.com/Simatwa/y2mate-api/blob/main/assets/cf_clearance_highlighted.png?raw=true)
81+
82+
> [!IMPORTANT]
83+
> Since the script impersonates only one browser, you will have to use **Chrome Browser** for *CF-CLEARANCE* extraction process.
84+
85+
The uncompromised usage will be:
86+
87+
`$ y2mate -f <mp3/mp4> -cf <CF-CLEARANCE> <youtube-link or video id or keyword>`
88+
89+
90+
To developers:
91+
92+
```python
93+
from y2mate_api import session
94+
95+
session.cookies.update(
96+
{
97+
"cf_clearance": cf_clearance_value
98+
}
99+
)
100+
```
101+
102+
As a workaround to explicitly declaring the CF-CLEARANCE along with other commands, export it to the environment as `Y2MATE_CF_CLEARANCE`.
103+
104+
By doing that, something like this will still get the work done:
105+
106+
`$ y2mate -f <mp3/mp4> <youtube-link or video id or keyword>`
107+
108+
> [!WARNING]
109+
> The CF-CLEARANCE expires after a short while, consider updating it more frequently.
110+
111+
112+
71113
<details>
72114

73115
<summary>
@@ -205,10 +247,10 @@ For more info run `$ y2mate -h`
205247
```
206248
usage: y2mate [-h] [-v] [-f mp3|mp4]
207249
[-q 4k|1080p|720p|480p|360p|240p|144p|auto|best|worst|mp3|m4a|.m4a|128kbps|192kbps|328kbps]
208-
[-r m4a|3gp|mp4|mp3] [-k [KEYWORD ...]] [-a [AUTHOR ...]]
209-
[-l LIMIT] [-d PATH] [-t TIMEOUT] [-c CHUNK] [-i PATH]
210-
[-o FORMAT] [-thr THREAD] [--disable-bar] [--confirm] [--unique]
211-
[--quiet] [--history] [--clear] [--resume] [--play]
250+
[-r m4a|3gp|mp4|mp3] [-k [KEYWORD ...]] [-a [AUTHOR ...]] [-l LIMIT]
251+
[-d PATH] [-t TIMEOUT] [-c CHUNK] [-i PATH] [-o FORMAT] [-cf COOKIE]
252+
[-thr THREAD] [--disable-bar] [--confirm] [--unique] [--quiet] [--history]
253+
[--clear] [--resume] [--play]
212254
[query ...]
213255
214256
Download youtube videos and audios by title or link
@@ -224,16 +266,15 @@ options:
224266
-q 4k|1080p|720p|480p|360p|240p|144p|auto|best|worst|mp3|m4a|.m4a|128kbps|192kbps|328kbps, --quality 4k|1080p|720p|480p|360p|240p|144p|auto|best|worst|mp3|m4a|.m4a|128kbps|192kbps|328kbps
225267
Media quality - auto
226268
-r m4a|3gp|mp4|mp3, --resolver m4a|3gp|mp4|mp3
227-
Other media formats incase of multiple options -
228-
mp4/mp3
269+
Other media formats incase of multiple options - mp4/mp3
229270
-k [KEYWORD ...], --keyword [KEYWORD ...]
230271
Media should contain this keywords - None
231272
-a [AUTHOR ...], --author [AUTHOR ...]
232273
Media author i.e YouTube channel name - None
233274
-l LIMIT, --limit LIMIT
234275
Total videos to be downloaded - 1
235276
-d PATH, --dir PATH Directory for saving the contents -
236-
/home/smartwa/git/Smartwa/I-learn-this
277+
/home/smartwa/git/smartwa/y2mate-api
237278
-t TIMEOUT, --timeout TIMEOUT
238279
Http request timeout in seconds - 30
239280
-c CHUNK, --chunk CHUNK
@@ -243,6 +284,8 @@ options:
243284
-o FORMAT, --output FORMAT
244285
Format for generating filename %(key)s :
245286
[title,vid,fquality,ftype] or 'pretty' - None
287+
-cf COOKIE, --cf-clearance COOKIE
288+
cf_clearance cookie value for y2mate.com
246289
-thr THREAD, --thread THREAD
247290
Download [x] amount of videos/audios at once - 1
248291
--disable-bar Disable download progress bar - False
302 KB
Loading

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ colorama==0.4.6
55
appdirs==1.4.4
66
click==8.1.3
77
brotli==1.1.0
8+
curl_cffi==0.7.2

setup.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from setuptools import setup
22

3-
version = "1.0.6"
3+
version = "1.2.0"
44
info = "Download youtube videos and audios by title or link"
55
author = "Smartwa"
66
repo = "https://github.com/Simatwa/y2mate-api"
@@ -24,8 +24,9 @@
2424
"appdirs==1.4.4",
2525
"click==8.1.3",
2626
"brotli==1.1.0",
27+
"curl_cffi==0.7.2",
2728
],
28-
python_requires=">=3.8",
29+
python_requires=">=3.9",
2930
long_description=open("README.md").read(),
3031
long_description_content_type="text/markdown",
3132
classifiers=[
@@ -42,7 +43,7 @@
4243
"Programming Language :: Python :: 3.9",
4344
"Programming Language :: Python :: 3.10",
4445
"Programming Language :: Python :: 3.11",
45-
"Programming Language :: Python :: 3.12"
46+
"Programming Language :: Python :: 3.12",
4647
],
4748
entry_points={
4849
"console_scripts": [

test.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# import requests
2+
3+
from curl_cffi import requests
4+
5+
headers = {
6+
"Accept": "*/*",
7+
"Accept-Encoding": "gzip, deflate, br, zstd",
8+
"Accept-Language": "en-US,en;q=0.5",
9+
# "Connection": "keep-alive",
10+
# "Content-Type" :"application/x-www-form-urlencoded; charset=UTF-8",
11+
# "Origin": "https://www.y2mate.com",
12+
"Referer": "https://www.y2mate.com/search/hello",
13+
# "Upgrade-Insecure-Requests": "1",
14+
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
15+
# "X-Requested-With": "XMLHttpRequest",
16+
}
17+
18+
payload = {
19+
"k_query": "https://youtu.be/akEZeHBOyko?si=3p2WreYh6-3EoIpc",
20+
"k_page": "home",
21+
"hl": "en",
22+
"q_auto": "0",
23+
}
24+
25+
session = requests.Session()
26+
session.headers.update(headers)
27+
session.cookies.update(
28+
{
29+
"cf_clearance": "0bKyUQFwyXTJ9IdaSCfUZ2_YsZtP9WpeCnjb2UXZZhs-1727812278-1.2.1.1-Almns7kQgsfC4S8oy08yZMvhZXZOa5M1kTxTMSunnnSoqT1qruuGUUzx0M_gnd2ps2qoJ0rOoL3k0TASKfA3dP4HPbFI11S9lNYvP0IjcieG3yFRqA5TSN0wVfJRkvYxMwPCUNVVTz9oAy9ARRGy_IyMNeQu2.NgSFUFJYD0h4v.DA0hwVwgrz4e2Kf5rjFp5ZCYZHMasuUZcnBMOVp9pQE63lBT6l7k0H902pFHvpqE7Y2Y19wHGeQq2FcywYSITB_QzyME2t1tLC1m53MvFrBnxfH5SQ2iIZF1hL7s4FihnPJQs32ik8kAm.TyewgT3MXs2xq1B_vIEAsvbSzQqjZ8scMrIeRppBwfL59JHvu23JkwwN_kqvK2mH1DhVQQ3FvWVbSerNqwjQjUll_iO5fBn00LzuScBep1aRLOHlI"
30+
}
31+
)
32+
33+
34+
def main():
35+
url = "https://www.y2mate.com/mates/en948/analyzeV2/ajax"
36+
resp = session.post(url, data=payload, impersonate="chrome")
37+
print(resp.status_code, resp.reason)
38+
39+
40+
if __name__ == "__main__":
41+
main()

y2mate_api/__init__.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,7 @@
99
from .main import second_query
1010
from .main import third_query
1111
from .main import appdir
12+
from .main import session
1213
from .downloader import Handler
1314

14-
__all__ = [
15-
"first_query",
16-
"second_query",
17-
"third_query",
18-
"Handler",
19-
"appdir"
20-
]
15+
__all__ = ["first_query", "second_query", "third_query", "Handler", "appdir", "session"]

y2mate_api/console.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import logging
33
from . import __version__, __info__, __disclaimer__
44
from .main import utils
5-
from os import getcwd, remove
5+
from os import getcwd, remove, getenv
66
from sys import exit
77
from .main import history_path, utils
88

@@ -112,6 +112,12 @@ def get_args():
112112
metavar="FORMAT",
113113
help="Format for generating filename %%(key)s : [title,vid,fquality,ftype] or 'pretty' - %(default)s",
114114
)
115+
parser.add_argument(
116+
"-cf",
117+
"--cf-clearance",
118+
help="cf_clearance cookie value for y2mate.com",
119+
metavar="COOKIE",
120+
)
115121
parser.add_argument(
116122
"-thr",
117123
"--thread",
@@ -185,9 +191,11 @@ def main():
185191
dir=args.dir,
186192
progress_bar=args.disable_bar == False,
187193
quiet=args.quiet,
188-
naming_format=f"%(title)s{' - %(fquality)s' if args.format=='mp4' else ''}.%(ftype)s"
189-
if str(args.output).lower() == "pretty"
190-
else args.output,
194+
naming_format=(
195+
f"%(title)s{' - %(fquality)s' if args.format=='mp4' else ''}.%(ftype)s"
196+
if str(args.output).lower() == "pretty"
197+
else args.output
198+
),
191199
chunk_size=args.chunk,
192200
play=args.play,
193201
format=args.format,
@@ -198,6 +206,11 @@ def main():
198206
author=h_mult_args(args.author),
199207
resume=args.resume,
200208
)
209+
cf_clearance_value = args.cf_clearance or getenv("Y2MATE_CF_CLEARANCE")
210+
if cf_clearance_value:
211+
from . import session
212+
213+
session.cookies.update({"cf_clearance": cf_clearance_value})
201214
logging.info(f"y2mate launched - v{__version__}")
202215
if args.input:
203216
for query in open(args.input).read().strip().split("\n"):

y2mate_api/downloader.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from sys import stdout
1616
from click import launch as launch_media, confirm as confirm_from_user
1717
import warnings
18+
import requests as requests_native
1819

1920
"""
2021
- query string
@@ -402,7 +403,9 @@ def save(
402403
current_downloaded_size / 1000000, 2
403404
) # convert to mb
404405

405-
resp = requests.get(third_dict["dlink"], stream=True, headers=mod_headers)
406+
resp = requests_native.get(
407+
third_dict["dlink"], stream=True, headers=mod_headers
408+
)
406409

407410
default_content_length = 0
408411
size_in_bytes = int(
@@ -433,8 +436,8 @@ def save(
433436
if any([save_to.startswith("/"), ":" in save_to])
434437
else path.join(getcwd(), dir, filename)
435438
)
436-
try_play_media = (
437-
lambda: launch_media(third_dict["saved_to"]) if play else None
439+
try_play_media = lambda: (
440+
launch_media(third_dict["saved_to"]) if play else None
438441
)
439442
saving_mode = "ab" if resume else "wb"
440443
if progress_bar:

y2mate_api/main.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import requests
1+
# import requests
2+
from curl_cffi import requests
23
import logging
34
from time import sleep
45
import json
@@ -8,11 +9,11 @@
89
from sys import exit
910

1011
__prog__ = "y2mate"
11-
session = requests.session()
12+
session = requests.Session()
1213

1314
headers = {
1415
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
15-
"User-Agent": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36",
16+
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
1617
"Accept-Encoding": "gzip, deflate, br",
1718
"Accept-Language": "en-US,en;q=0.9",
1819
"referer": "https://y2mate.com",
@@ -67,12 +68,14 @@ def main(*args, **kwargs):
6768
@staticmethod
6869
def get(*args, **kwargs):
6970
r"""Sends http get request"""
71+
kwargs["impersonate"] = "chrome"
7072
resp = session.get(*args, **kwargs)
7173
return all([resp.ok, "application/json" in resp.headers["content-type"]]), resp
7274

7375
@staticmethod
7476
def post(*args, **kwargs):
7577
r"""Sends http post request"""
78+
kwargs["impersonate"] = "chrome"
7679
resp = session.post(*args, **kwargs)
7780
return all([resp.ok, "application/json" in resp.headers["content-type"]]), resp
7881

@@ -183,7 +186,11 @@ def main(self, timeout=30):
183186
self.processed = True
184187
else:
185188
logging.debug(f"{resp.headers.get('content-type')} - {resp.content}")
186-
logging.error(f"First query failed - [{resp.status_code} : {resp.reason}")
189+
logging.error(f"First query failed - [{resp.status_code} : {resp.reason}]")
190+
if session.cookies.get('cf_clearance'):
191+
logging.info('Seems like CF-CLEARANCE cookie has expired!')
192+
else:
193+
logging.info('Try passing CF-CLEARANCE cookie.')
187194
return self
188195

189196

@@ -322,6 +329,7 @@ def __init__(self, query_two: object):
322329
self.qualities = {
323330
self.formats[0]: [
324331
"4k",
332+
"2k",
325333
"1080p",
326334
"720p",
327335
"480p",

0 commit comments

Comments
 (0)