Eporner_API 1.9.7__tar.gz → 2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Eporner_API
3
- Version: 1.9.7
3
+ Version: 2.0
4
4
  Summary: A Python API for the Porn Site Eporner.com
5
5
  Author: Johannes Habel
6
6
  Author-email: Johannes Habel <EchterAlsFake@proton.me>
@@ -10,8 +10,6 @@ Classifier: Programming Language :: Python
10
10
  Requires-Dist: bs4
11
11
  Requires-Dist: eaf-base-api
12
12
  Requires-Dist: lxml ; extra == 'full'
13
- Requires-Dist: httpx[http2] ; extra == 'full'
14
- Requires-Dist: httpx[socks] ; extra == 'full'
15
13
  Requires-Python: >=3.9
16
14
  Project-URL: Homepage, https://github.com/EchterAlsFake/EPorner_API
17
15
  Provides-Extra: full
@@ -3,6 +3,7 @@ import html
3
3
  import json
4
4
  import os.path
5
5
  import logging
6
+ import asyncio
6
7
  import argparse
7
8
  import traceback
8
9
  import threading
@@ -33,7 +34,7 @@ from bs4 import BeautifulSoup
33
34
  from urllib.parse import urljoin
34
35
  from functools import cached_property
35
36
  from base_api.modules.config import RuntimeConfig
36
- from typing import Generator, Union, Optional, List
37
+ from typing import AsyncGenerator, Generator, Union, Optional, List
37
38
  from base_api.base import BaseCore, setup_logger, Helper
38
39
 
39
40
  """
@@ -102,21 +103,27 @@ def _choose_quality_from_list(available: List[str | int], target: Union[str, int
102
103
 
103
104
 
104
105
  class Video:
105
- def __init__(self, url: str, enable_html_scraping: bool = True, core: Optional[BaseCore] = None):
106
+ def __init__(self, url: str, enable_html_scraping: bool = True, core: Optional[BaseCore] = None, html_content=None):
106
107
  self.core = core
107
108
  self.url = url
108
109
  self.enable_html = enable_html_scraping
109
- self.html_content = None
110
+ self.html_content = html_content
110
111
  self.logger = setup_logger(name="EPorner API - [Video]", log_file=None, level=logging.CRITICAL)
111
- self.json_data = self.raw_json_data()
112
+ self.json_data = {}
113
+ self.html_json_data = {}
114
+
115
+ async def init(self):
116
+ self.json_data = await self.get_raw_json_data()
112
117
  if self.enable_html:
113
- self.request_html_content()
118
+ if not self.html_content:
119
+ await self.request_html_content()
114
120
  is_removed = REGEX_VIDEO_DISABLED.findall(self.html_content)
115
121
  for _ in is_removed:
116
122
  if _ == "deletedfile":
117
123
  raise VideoDisabled("Video has been removed because of a Copyright claim")
118
124
 
119
125
  self.html_json_data = self.extract_json_from_html()
126
+ return self
120
127
 
121
128
  def enable_logging(self, log_file: str, level, log_ip: str = None, log_port: int = None):
122
129
  self.logger = setup_logger(name="EPorner API - [Video]", log_file=log_file, level=level, http_ip=log_ip, http_port=log_port)
@@ -144,13 +151,13 @@ class Video:
144
151
  else:
145
152
  return self.url # Assuming this is a video ID (hopefully)
146
153
 
147
- def raw_json_data(self):
154
+ async def get_raw_json_data(self):
148
155
  """
149
156
  Uses the V2 API to retrieve information from a video
150
157
  :return:
151
158
  """
152
159
 
153
- data = self.core.fetch(f"{ROOT_URL}{API_VIDEO_ID}?id={self.video_id}&thumbsize=medium&format=json")
160
+ data = await self.core.fetch(f"{ROOT_URL}{API_VIDEO_ID}?id={self.video_id}&thumbsize=medium&format=json")
154
161
  parsed_data = json.loads(data)
155
162
  return parsed_data
156
163
 
@@ -212,11 +219,11 @@ class Video:
212
219
  The following methods are using HTML scraping. This is against the ToS from EPorner.com!
213
220
  """
214
221
 
215
- def request_html_content(self):
222
+ async def request_html_content(self):
216
223
  if not self.enable_html:
217
224
  raise HTML_IS_DISABLED("HTML content is disabled! See Documentation for more details")
218
225
 
219
- self.html_content = html.unescape(self.core.fetch(self.url))
226
+ self.html_content = html.unescape(await self.core.fetch(self.url))
220
227
 
221
228
 
222
229
  def extract_json_from_html(self):
@@ -397,7 +404,7 @@ JSONDecodeError: I need your help to fix this error. Please report the URL you'v
397
404
  self.logger.info(f"Using direct download link: {full_url} ({chosen_height}p)")
398
405
  return full_url
399
406
 
400
- def download(self, quality, path, callback=None, mode=Encoding.mp4_h264, no_title=False, use_workaround=False,
407
+ async def download(self, quality, path, callback=None, mode=Encoding.mp4_h264, no_title=False, use_workaround=False,
401
408
  stop_event: threading.Event = None):
402
409
  if not self.enable_html:
403
410
  raise HTML_IS_DISABLED("HTML content is disabled! See Documentation for more details")
@@ -408,13 +415,13 @@ JSONDecodeError: I need your help to fix this error. Please report the URL you'v
408
415
 
409
416
  url = self.direct_download_link(quality, mode)
410
417
  if use_workaround:
411
- response_redirect_url = self.core.fetch(self.direct_download_link(quality, mode),
418
+ response_redirect_url = await self.core.fetch(self.direct_download_link(quality, mode),
412
419
  allow_redirects=True, get_response=True) # Sometimes the site trolls me
413
420
 
414
421
  url = response_redirect_url.url
415
422
 
416
423
  try:
417
- self.core.legacy_download(url=url, callback=callback, path=path, stop_event=stop_event)
424
+ await self.core.legacy_download(url=url, callback=callback, path=path, stop_event=stop_event)
418
425
  return True
419
426
 
420
427
  except Exception:
@@ -425,18 +432,23 @@ JSONDecodeError: I need your help to fix this error. Please report the URL you'v
425
432
 
426
433
 
427
434
  class Pornstar(Helper):
428
- def __init__(self, url: str, enable_html_scraping: bool = False, core: Optional[BaseCore] = None):
435
+ def __init__(self, url: str, enable_html_scraping: bool = False, core: Optional[BaseCore] = None, html_content=None):
429
436
  super().__init__(core=core, video=Video)
430
437
  self.core = core
431
438
  self.url = url
432
439
  self.enable_html_scraping = enable_html_scraping
433
440
  self.logger = setup_logger(name="EPorner API - [Pornstar]", log_file=None, level=logging.CRITICAL)
434
- self.html_content = self.core.fetch(self.url)
441
+ self.html_content = html_content
442
+
443
+ async def init(self):
444
+ if not self.html_content:
445
+ self.html_content = await self.core.fetch(self.url)
446
+ return self
435
447
 
436
448
  def enable_logging(self, log_file: str, level, log_ip: str = None, log_port: int = None):
437
449
  self.logger = setup_logger(name="EPorner API - [Pornstar]", log_file=log_file, level=level, http_ip=log_ip, http_port=log_port)
438
450
 
439
- def videos(self, pages: int = 0, videos_concurrency: int = None, pages_concurrency: int = None) -> Generator[Video, None, None]:
451
+ async def videos(self, pages: int = 0, videos_concurrency: int = None, pages_concurrency: int = None) -> AsyncGenerator[Video, None]:
440
452
  if pages == 0:
441
453
  video_amount = str(self.video_amount).replace(",", "")
442
454
  pages = round(int(video_amount)) / 37 # One page contains 37 videos
@@ -446,8 +458,9 @@ class Pornstar(Helper):
446
458
 
447
459
  pages = round(pages) # Dont ask
448
460
  page_urls = [urljoin(f"{self.url}/", str(page)) for page in range(1, pages + 1)]
449
- yield from self.iterator(page_urls=page_urls, extractor=extractor, pages_concurrency=pages_concurrency,
450
- videos_concurrency=videos_concurrency)
461
+ async for video in self.iterator(page_urls=page_urls, extractor=extractor, pages_concurrency=pages_concurrency,
462
+ videos_concurrency=videos_concurrency):
463
+ yield await video.init()
451
464
 
452
465
  @cached_property
453
466
  def name(self) -> str:
@@ -564,41 +577,45 @@ class Client(Helper):
564
577
  def enable_logging(self, log_file: str, level, log_ip: str = None, log_port: int = None):
565
578
  self.logger = setup_logger(name="EPorner API - [Client]", log_file=log_file, level=level, http_ip=log_ip, http_port=log_port)
566
579
 
567
- def get_video(self, url: str, enable_html_scraping: bool = True) -> Video:
580
+ async def get_video(self, url: str, enable_html_scraping: bool = True) -> Video:
568
581
  """Returns the Video object for a given URL"""
569
582
  self.logger.info(f"Returning video object for: {url} HTML Scraping -> {enable_html_scraping}")
570
- return Video(url, enable_html_scraping=enable_html_scraping, core=self.core)
583
+ video = Video(url, enable_html_scraping=enable_html_scraping, core=self.core)
584
+ return await video.init()
571
585
 
572
- def search_videos(self, query: str, sorting_gay: Union[str, Gay], sorting_order: Union[str, Order],
586
+ async def search_videos(self, query: str, sorting_gay: Union[str, Gay], sorting_order: Union[str, Order],
573
587
  sorting_low_quality: Union[str, LowQuality],
574
- page: int, per_page: int, enable_html_scraping: bool = True) -> Generator[Video, None, None]:
588
+ page: int, per_page: int, enable_html_scraping: bool = True) -> AsyncGenerator[Video, None]:
575
589
 
576
- response = self.core.fetch(f"{ROOT_URL}{API_SEARCH}?query={query}&per_page={per_page}&%page={page}"
590
+ response = await self.core.fetch(f"{ROOT_URL}{API_SEARCH}?query={query}&per_page={per_page}&%page={page}"
577
591
  f"&thumbsize=medium&order={sorting_order}&gay={sorting_gay}&lq="
578
592
  f"{sorting_low_quality}&format=json")
579
593
 
580
594
  json_data = json.loads(response)
581
595
  for video_ in json_data.get("videos", []): # Don't know why this works lmao
582
596
  id_ = video_["url"]
583
- yield Video(id_, enable_html_scraping, core=self.core)
597
+ video = Video(id_, enable_html_scraping, core=self.core)
598
+ yield await video.init()
584
599
 
585
- def get_videos_by_category(self, category: Union[str, Category], enable_html_scraping: bool = False,
586
- videos_concurrency: int = None, pages_concurrency: int = None) -> Generator[Video, None, None]:
600
+ async def get_videos_by_category(self, category: Union[str, Category], enable_html_scraping: bool = False,
601
+ videos_concurrency: int = None, pages_concurrency: int = None) -> AsyncGenerator[Video, None]:
587
602
 
588
603
  page_urls = [f"{ROOT_URL}cat/{category}/{page}" for page in range(1, 100)]
589
604
 
590
605
  videos_concurrency = videos_concurrency or self.core.config.videos_concurrency
591
606
  pages_concurrency = pages_concurrency or self.core.config.pages_concurrency
592
- yield from self.iterator(page_urls=page_urls, videos_concurrency=videos_concurrency,
593
- pages_concurrency=pages_concurrency, extractor=extractor)
607
+ async for video in self.iterator(page_urls=page_urls, videos_concurrency=videos_concurrency,
608
+ pages_concurrency=pages_concurrency, extractor=extractor):
609
+ yield await video.init()
594
610
 
595
611
 
596
- def get_pornstar(self, url: str, enable_html_scraping: bool = True) -> Pornstar:
612
+ async def get_pornstar(self, url: str, enable_html_scraping: bool = True) -> Pornstar:
597
613
  self.logger.info(f"Returning Pornstar object for: {url} HTML Scraping -> {enable_html_scraping}")
598
- return Pornstar(url, enable_html_scraping, core=self.core)
614
+ pornstar = Pornstar(url, enable_html_scraping, core=self.core)
615
+ return await pornstar.init()
599
616
 
600
617
 
601
- def main():
618
+ async def run_main():
602
619
  parser = argparse.ArgumentParser(description="API Command Line Interface")
603
620
  parser.add_argument("--download", metavar="URL (str)", type=str, help="URL to download from")
604
621
  parser.add_argument("--quality", metavar="best,half,worst", type=str, help="The video quality (best,half,worst)",
@@ -615,8 +632,8 @@ def main():
615
632
 
616
633
  if args.download:
617
634
  client = Client()
618
- video = client.get_video(args.download, enable_html_scraping=True)
619
- video.download(quality=args.quality, path=args.output, no_title=no_title)
635
+ video = await client.get_video(args.download, enable_html_scraping=True)
636
+ await video.download(quality=args.quality, path=args.output, no_title=no_title)
620
637
 
621
638
  if args.file:
622
639
  videos = []
@@ -626,11 +643,14 @@ def main():
626
643
  content = file.read().splitlines()
627
644
 
628
645
  for url in content:
629
- videos.append(client.get_video(url, enable_html_scraping=True))
646
+ videos.append(await client.get_video(url, enable_html_scraping=True))
630
647
 
631
648
  for video in videos:
632
- video.download(quality=args.quality, path=args.output, no_title=no_title)
649
+ await video.download(quality=args.quality, path=args.output, no_title=no_title)
633
650
 
634
651
 
652
+ def main():
653
+ asyncio.run(run_main())
654
+
635
655
  if __name__ == "__main__":
636
656
  main()
@@ -1,31 +1,34 @@
1
- import time
2
-
1
+ import pytest
3
2
  from ..eporner_api import Client, Category
4
3
  from base_api import BaseCore
5
- core = BaseCore()
6
- core.config.pages_concurrency = 1
7
- core.config.videos_concurrency = 1
8
-
9
4
 
10
- def test_category():
5
+ @pytest.mark.asyncio
6
+ async def test_category():
7
+ core = BaseCore()
8
+ core.config.pages_concurrency = 1
9
+ core.config.videos_concurrency = 1
10
+
11
11
  videos_1 = Client(core).get_videos_by_category(category=Category.JAPANESE)
12
12
  videos_2 = Client(core).get_videos_by_category(category=Category.HD)
13
13
  videos_3 = Client(core).get_videos_by_category(category=Category.BLONDE)
14
14
 
15
- for idx, video in enumerate(videos_1):
15
+ idx = 0
16
+ async for video in videos_1:
16
17
  if idx == 3:
17
18
  break
18
-
19
19
  assert isinstance(video.title, str) and len(video.title) > 0
20
+ idx += 1
20
21
 
21
- for idx, video in enumerate(videos_2):
22
+ idx = 0
23
+ async for video in videos_2:
22
24
  if idx == 3:
23
25
  break
24
-
25
26
  assert isinstance(video.title, str) and len(video.title) > 0
27
+ idx += 1
26
28
 
27
- for idx, video in enumerate(videos_3):
29
+ idx = 0
30
+ async for video in videos_3:
28
31
  if idx == 3:
29
32
  break
30
-
31
33
  assert isinstance(video.title, str) and len(video.title) > 0
34
+ idx += 1
@@ -1,27 +1,28 @@
1
+ import pytest
1
2
  from ..eporner_api import Client
2
- import time
3
- url = "https://www.eporner.com/pornstar/riley-reid/"
4
3
  from base_api import BaseCore
5
- core = BaseCore()
6
- core.config.pages_concurrency = 1
7
- core.config.videos_concurrency = 1
8
4
 
9
- pornstar = Client(core).get_pornstar(url, enable_html_scraping=True)
5
+ @pytest.mark.asyncio
6
+ async def test_pornstar():
7
+ url = "https://www.eporner.com/pornstar/riley-reid/"
8
+ core = BaseCore()
9
+ core.config.pages_concurrency = 1
10
+ core.config.videos_concurrency = 1
11
+ pornstar = await Client(core).get_pornstar(url, enable_html_scraping=True)
10
12
 
11
-
12
- def test_videos():
13
13
  videos = pornstar.videos(pages=1)
14
-
15
- for idx, video in enumerate(videos):
14
+
15
+ idx = 0
16
+ async for video in videos:
16
17
  assert isinstance(video.title, str) and len(video.title) > 3
17
18
  if idx == 5:
18
19
  break
20
+ idx += 1
19
21
 
20
- def test_information():
21
22
  assert isinstance(pornstar.pornstar_rank, str) and len(pornstar.pornstar_rank) >= 1
22
23
  assert isinstance(pornstar.aliases, list) and len(pornstar.aliases) > 1
23
24
  assert isinstance(pornstar.biography, str) and len(pornstar.biography) > 10
24
- assert isinstance(pornstar.age, str) and len(pornstar.age) >= 2 # would be weird if this is 1-9 lmao (just kidding)
25
+ assert isinstance(pornstar.age, str) and len(pornstar.age) >= 2
25
26
  assert isinstance(pornstar.cup, str) and len(pornstar.cup) >= 1
26
27
  assert isinstance(pornstar.country, str) and len(pornstar.country) >= 2
27
28
  assert isinstance(pornstar.weight, str) and len(pornstar.weight) >= 2
@@ -1,48 +1,53 @@
1
+ import pytest
1
2
  from ..eporner_api import Client, Gay, Order, LowQuality
2
- import time
3
3
 
4
- client = Client()
4
+
5
5
  query = "Mia Khalifa"
6
- pages = 2
7
- per_page = 10
6
+ pages = 1
7
+ per_page = 1
8
+
9
+ @pytest.fixture
10
+ def client():
11
+ return Client()
8
12
 
9
- def test_search_1():
13
+ @pytest.mark.asyncio
14
+ async def test_search_1(client):
10
15
  videos = client.search_videos(query, page=pages, per_page=per_page, sorting_gay=Gay.exclude_gay_content, sorting_order=Order.top_rated, sorting_low_quality=LowQuality.exclude_low_quality_content)
11
- for video in videos:
16
+ async for video in videos:
12
17
  assert len(video.title) > 0
13
18
 
14
-
15
- def test_search_2():
19
+ @pytest.mark.asyncio
20
+ async def test_search_2(client):
16
21
  videos = client.search_videos(query, page=pages, per_page=per_page, sorting_gay=Gay.only_gay_content, sorting_order=Order.latest, sorting_low_quality=LowQuality.only_low_quality_content)
17
- for video in videos:
22
+ async for video in videos:
18
23
  assert len(video.title) > 0
19
24
 
20
-
21
- def test_search_3():
25
+ @pytest.mark.asyncio
26
+ async def test_search_3(client):
22
27
  videos = client.search_videos(query, page=pages, per_page=per_page, sorting_gay=Gay.include_gay_content, sorting_order=Order.longest, sorting_low_quality=LowQuality.include_low_quality_content)
23
- for video in videos:
28
+ async for video in videos:
24
29
  assert len(video.title) > 0
25
30
 
26
-
27
- def test_search_4():
31
+ @pytest.mark.asyncio
32
+ async def test_search_4(client):
28
33
  videos = client.search_videos(query, page=pages, per_page=pages, sorting_gay=Gay.exclude_gay_content, sorting_order=Order.shortest, sorting_low_quality=LowQuality.include_low_quality_content)
29
- for video in videos:
34
+ async for video in videos:
30
35
  assert len(video.title) > 0
31
36
 
32
-
33
- def test_search_5():
37
+ @pytest.mark.asyncio
38
+ async def test_search_5(client):
34
39
  videos = client.search_videos(query, page=pages, per_page=per_page, sorting_order=Gay.include_gay_content, sorting_gay=Order.top_weekly, sorting_low_quality=LowQuality.include_low_quality_content)
35
- for video in videos:
40
+ async for video in videos:
36
41
  assert len(video.title) > 0
37
42
 
38
-
39
- def test_search_6():
43
+ @pytest.mark.asyncio
44
+ async def test_search_6(client):
40
45
  videos = client.search_videos(query, page=pages, per_page=per_page, sorting_order=Order.most_popular, sorting_low_quality=LowQuality.include_low_quality_content, sorting_gay=Gay.only_gay_content)
41
- for video in videos:
46
+ async for video in videos:
42
47
  assert len(video.title) > 0
43
48
 
44
-
45
- def test_search_7():
49
+ @pytest.mark.asyncio
50
+ async def test_search_7(client):
46
51
  videos = client.search_videos(query, page=pages, per_page=per_page, sorting_gay=Gay.include_gay_content, sorting_order=Order.top_monthly, sorting_low_quality=LowQuality.include_low_quality_content)
47
- for video in videos:
52
+ async for video in videos:
48
53
  assert len(video.title) > 0
@@ -1,70 +1,26 @@
1
+ import pytest
1
2
  from ..eporner_api import Client, Encoding, NotAvailable
2
- import time
3
- url = "https://www.eporner.com/video-bTwP6vsFj5U/human-anal-sex-toy/"
4
- video = Client().get_video(url, enable_html_scraping=True)
5
- time.sleep(5) # Lmao
6
3
 
7
- def test_title():
4
+ @pytest.mark.asyncio
5
+ async def test_video():
6
+ url = "https://www.eporner.com/video-bTwP6vsFj5U/human-anal-sex-toy/"
7
+ video = await Client().get_video(url, enable_html_scraping=True)
8
8
  assert isinstance(video.title, str) and len(video.title) > 0
9
-
10
-
11
- def test_video_id():
12
9
  assert isinstance(video.video_id, str) and len(video.video_id) > 0
13
-
14
-
15
- def test_tags():
16
10
  assert isinstance(video.tags, list) and len(video.tags) > 0
17
-
18
-
19
- def test_views():
20
11
  assert isinstance(video.views, int) and video.views > 0
21
-
22
-
23
- def test_rate():
24
12
  assert isinstance(video.rate, str) and len(video.rate) > 0
25
-
26
-
27
- def test_publish_date():
28
13
  assert isinstance(video.publish_date, str) and len(video.publish_date) > 0
29
-
30
-
31
- def test_length_seconds():
32
- assert isinstance(video.length, int) > 0
33
-
34
-
35
- def test_length_minutes():
14
+ assert isinstance(video.length, int) and video.length > 0
36
15
  assert isinstance(video.length_minutes, str) and len(video.length_minutes) > 0
37
-
38
-
39
- def test_embed_url():
40
16
  assert isinstance(video.embed_url, str) and len(video.embed_url) > 0
41
-
42
-
43
- def test_thumbnails():
44
17
  assert isinstance(video.thumbnail, str) and len(video.thumbnail) > 0
45
-
46
-
47
- def test_bitrate():
48
18
  assert isinstance(video.bitrate, str) and len(video.bitrate) > 0
49
-
50
-
51
- def test_source_video_url():
52
19
  assert isinstance(video.source_video_url, str) and len(video.source_video_url) > 0
53
-
54
-
55
- def test_rating():
56
20
  assert isinstance(video.rating, str) and len(video.rating) > 0
57
-
58
-
59
- def test_rating_count():
60
21
  assert isinstance(video.rating_count, str) and len(video.rating_count) > 0
61
-
62
-
63
- def test_author():
64
22
  assert isinstance(video.author, str) and len(video.author) > 0
65
23
 
66
-
67
- def test_direct_download_url():
68
24
  assert isinstance(video.direct_download_link(quality=2160, mode=Encoding.mp4_h264), str)
69
25
  assert isinstance(video.direct_download_link(quality="half", mode=Encoding.mp4_h264), str)
70
26
  assert isinstance(video.direct_download_link(quality="worst", mode=Encoding.mp4_h264), str)
@@ -72,6 +28,5 @@ def test_direct_download_url():
72
28
  assert isinstance(video.direct_download_link(quality="best", mode=Encoding.av1), str)
73
29
  assert isinstance(video.direct_download_link(quality="half", mode=Encoding.av1), str)
74
30
  assert isinstance(video.direct_download_link(quality="worst", mode=Encoding.av1), str)
75
-
76
31
  except NotAvailable:
77
32
  pass
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "Eporner_API"
7
- version = "1.9.7"
7
+ version = "2.0"
8
8
  description = "A Python API for the Porn Site Eporner.com"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.9" # 3.9 due to httpx requirements
@@ -15,14 +15,14 @@ authors = [
15
15
  ]
16
16
  dependencies = [
17
17
  "bs4",
18
- "eaf_base_api",
18
+ "eaf-base-api",
19
19
  ]
20
20
  classifiers = [
21
21
  "Programming Language :: Python",
22
22
  ]
23
23
 
24
24
  [project.optional-dependencies]
25
- full = ["lxml", "httpx[http2]", "httpx[socks]"]
25
+ full = ["lxml"]
26
26
 
27
27
  [project.urls]
28
28
  Homepage = "https://github.com/EchterAlsFake/EPorner_API"
File without changes
File without changes