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.
- {eporner_api-1.9.7 → eporner_api-2.0}/PKG-INFO +1 -3
- {eporner_api-1.9.7 → eporner_api-2.0}/eporner_api/eporner_api.py +54 -34
- {eporner_api-1.9.7 → eporner_api-2.0}/eporner_api/tests/test_category.py +16 -13
- {eporner_api-1.9.7 → eporner_api-2.0}/eporner_api/tests/test_pornstar.py +13 -12
- {eporner_api-1.9.7 → eporner_api-2.0}/eporner_api/tests/test_search.py +29 -24
- {eporner_api-1.9.7 → eporner_api-2.0}/eporner_api/tests/test_video.py +6 -51
- {eporner_api-1.9.7 → eporner_api-2.0}/pyproject.toml +3 -3
- {eporner_api-1.9.7 → eporner_api-2.0}/LICENSE +0 -0
- {eporner_api-1.9.7 → eporner_api-2.0}/README.md +0 -0
- {eporner_api-1.9.7 → eporner_api-2.0}/eporner_api/__init__.py +0 -0
- {eporner_api-1.9.7 → eporner_api-2.0}/eporner_api/modules/__init__.py +0 -0
- {eporner_api-1.9.7 → eporner_api-2.0}/eporner_api/modules/consts.py +0 -0
- {eporner_api-1.9.7 → eporner_api-2.0}/eporner_api/modules/errors.py +0 -0
- {eporner_api-1.9.7 → eporner_api-2.0}/eporner_api/modules/locals.py +0 -0
- {eporner_api-1.9.7 → eporner_api-2.0}/eporner_api/modules/progressbar.py +0 -0
- {eporner_api-1.9.7 → eporner_api-2.0}/eporner_api/modules/sorting.py +0 -0
- {eporner_api-1.9.7 → eporner_api-2.0}/eporner_api/tests/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Eporner_API
|
|
3
|
-
Version:
|
|
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 =
|
|
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 =
|
|
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.
|
|
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
|
|
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 =
|
|
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) ->
|
|
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
|
-
|
|
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
|
-
|
|
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) ->
|
|
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
|
-
|
|
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) ->
|
|
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
|
-
|
|
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
|
-
|
|
614
|
+
pornstar = Pornstar(url, enable_html_scraping, core=self.core)
|
|
615
|
+
return await pornstar.init()
|
|
599
616
|
|
|
600
617
|
|
|
601
|
-
def
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
4
|
+
|
|
5
5
|
query = "Mia Khalifa"
|
|
6
|
-
pages =
|
|
7
|
-
per_page =
|
|
6
|
+
pages = 1
|
|
7
|
+
per_page = 1
|
|
8
|
+
|
|
9
|
+
@pytest.fixture
|
|
10
|
+
def client():
|
|
11
|
+
return Client()
|
|
8
12
|
|
|
9
|
-
|
|
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
|
-
|
|
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 = "
|
|
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
|
-
"
|
|
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"
|
|
25
|
+
full = ["lxml"]
|
|
26
26
|
|
|
27
27
|
[project.urls]
|
|
28
28
|
Homepage = "https://github.com/EchterAlsFake/EPorner_API"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|