eaf_base_api 4.1.0__tar.gz → 4.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: eaf_base_api
3
- Version: 4.1.0
3
+ Version: 4.2.0
4
4
  Summary: A base API for EchterAlsFake's Porn APIs
5
5
  Author: Johannes Habel
6
6
  Author-email: Johannes Habel <EchterAlsFake@proton.me>
@@ -34,7 +34,63 @@ support, and there was a lot of code that I would have rewritten in every API ag
34
34
  package. The `BaseCore` class does all the necessary stuff like m3u8 parsing, a great caching system, network request
35
35
  fetching with retry attempts and proxy support.
36
36
 
37
- # Documentation (IMPORTANT!)
37
+ ## Consistent errors and logging (4.2)
38
+
39
+ Configure logging once in your application to capture all 15 provider APIs and
40
+ the base library:
41
+
42
+ ```python
43
+ import logging
44
+ from base_api import configure_app_logging, DownloadFailed
45
+
46
+ configure_app_logging(level=logging.WARNING, log_file="api-errors.log")
47
+
48
+ # Inside your async application:
49
+ try:
50
+ await video.download(configuration)
51
+ except DownloadFailed as error:
52
+ print(error.api, error.class_name, error.url)
53
+ # The failure and its full traceback have already been logged.
54
+ ```
55
+
56
+ Logs use Python module names, such as `xvideos_api.api` and `base_api.base`.
57
+ Request, source-loading, and download logs include the qualified class name and
58
+ URL in the message and in `LogRecord.class_name` / `LogRecord.url`. For example:
59
+
60
+ ```text
61
+ ERROR xvideos_api.api [class=xvideos_api.api.Video url=https://example.test/video/123] Download failed: disk full
62
+ Traceback (most recent call last):
63
+ ...
64
+ OSError: disk full
65
+ ```
66
+
67
+ Network logs identify the actual request URL, including CDN/segment URLs; media
68
+ errors also identify the original video page. Context stays separate for
69
+ concurrent downloads and follows parsing work into worker threads. Creating a
70
+ client/core does not install handlers or override your application's log level.
71
+ Without configuration, Python's fallback logging handler still prints warnings
72
+ and errors to stderr.
73
+
74
+ Catch common failures from `base_api`: `DownloadFailed`, `NetworkError`,
75
+ `NotFound`, `ProxyError`, `BotDetection`, `VideoUnavailable`, `LoginFailed`, and
76
+ `RegionBlocked`. Existing provider exception imports remain compatible,
77
+ including Pornhub's `PornhubAPIError` hierarchy. Translated exceptions preserve
78
+ the original exception as `__cause__`.
79
+
80
+ Ordinary download failures now raise `DownloadFailed` instead of returning
81
+ `False`. A stop signal raises `base_api.modules.errors.DownloadCancelled`;
82
+ async task cancellation propagates unchanged. Neither is logged as an error.
83
+ With `DownloadConfigHLS(return_report=True)`, incomplete/cancelled downloads
84
+ still return their explicit `DownloadReport`; unexpected exceptions still raise.
85
+
86
+ Shared request translation, download error handling, HLS preparation, and output
87
+ configuration live in `base_api.modules.provider`. The identical Tube8 and
88
+ Thumbzilla result-grid extraction lives in `base_api.modules.static_functions`.
89
+ Site-specific parsing, quality selection, and fallback behavior stay in each
90
+ provider. Updated providers require `eaf-base-api>=4.2.0`; update the base library
91
+ alongside them.
92
+
93
+ # Documentation (IMPORTANT!)
38
94
  > [!IMPORTANT]
39
95
  > Configuring eaf_base_api is necessary if you use any of my Porn APIs, because they all depend on this project.
40
96
  > Please read through the documentation to learn how `PROXIES`, `CACHING` and `LOGGING` etc... work!
@@ -154,7 +210,10 @@ stream = helper.iterator(
154
210
  async with stream:
155
211
  async for result in stream:
156
212
  if not result.succeeded:
157
- logger.error("%s failed: %s", result.stage, result.error)
213
+ logger.error(
214
+ "%s failed for %s: %s", result.stage, result.url, result.error,
215
+ exc_info=(type(result.error), result.error, result.error.__traceback__),
216
+ )
158
217
  continue
159
218
  video = result.unwrap()
160
219
  ```
@@ -165,6 +224,31 @@ or `ErrorMode.RAISE`. `RetryPolicy` provides a strict maximum attempt count and
165
224
  optional exponential delay; the independent page and item handlers return an
166
225
  `ErrorAction` and cannot create an unbounded retry loop.
167
226
 
227
+ ## Error logging
228
+
229
+ Configure logging once in the application to capture all provider and base API logs:
230
+
231
+ ```python
232
+ import logging
233
+ from base_api.modules.logger import configure_app_logging
234
+
235
+ configure_app_logging(log_file="api.log", level=logging.INFO)
236
+ ```
237
+
238
+ Failures include the operation, video/page URL, and original traceback. The default
239
+ formatter also shows the file, line, and function. Media source failures are logged
240
+ even when a caller catches the exception; iterator failures are logged even when
241
+ the configured policy skips or yields them. Provider CLIs configure console logging
242
+ automatically.
243
+
244
+ Provider request and download exceptions use the shared types in
245
+ `base_api.modules.errors`, preserving the original exception as `__cause__`.
246
+ Pornhub's existing exception classes remain catchable as `PornhubAPIError` and as
247
+ their shared equivalents. `ScraperException` also inherits `BaseScraperError`.
248
+ Download preparation errors now carry the video URL in `DownloadFailed`, and
249
+ explicit cancellation remains `DownloadCancelled` or `asyncio.CancelledError`.
250
+ Existing boolean/download-report results from the base downloader remain supported.
251
+
168
252
  # Can I use this for myself?
169
253
  Yes, you can, but I may change stuff here and there from time to time, and it would maybe break your project.
170
254
  I would not recommend you to install and use it as a package, but just copy the code you need.
@@ -14,7 +14,63 @@ support, and there was a lot of code that I would have rewritten in every API ag
14
14
  package. The `BaseCore` class does all the necessary stuff like m3u8 parsing, a great caching system, network request
15
15
  fetching with retry attempts and proxy support.
16
16
 
17
- # Documentation (IMPORTANT!)
17
+ ## Consistent errors and logging (4.2)
18
+
19
+ Configure logging once in your application to capture all 15 provider APIs and
20
+ the base library:
21
+
22
+ ```python
23
+ import logging
24
+ from base_api import configure_app_logging, DownloadFailed
25
+
26
+ configure_app_logging(level=logging.WARNING, log_file="api-errors.log")
27
+
28
+ # Inside your async application:
29
+ try:
30
+ await video.download(configuration)
31
+ except DownloadFailed as error:
32
+ print(error.api, error.class_name, error.url)
33
+ # The failure and its full traceback have already been logged.
34
+ ```
35
+
36
+ Logs use Python module names, such as `xvideos_api.api` and `base_api.base`.
37
+ Request, source-loading, and download logs include the qualified class name and
38
+ URL in the message and in `LogRecord.class_name` / `LogRecord.url`. For example:
39
+
40
+ ```text
41
+ ERROR xvideos_api.api [class=xvideos_api.api.Video url=https://example.test/video/123] Download failed: disk full
42
+ Traceback (most recent call last):
43
+ ...
44
+ OSError: disk full
45
+ ```
46
+
47
+ Network logs identify the actual request URL, including CDN/segment URLs; media
48
+ errors also identify the original video page. Context stays separate for
49
+ concurrent downloads and follows parsing work into worker threads. Creating a
50
+ client/core does not install handlers or override your application's log level.
51
+ Without configuration, Python's fallback logging handler still prints warnings
52
+ and errors to stderr.
53
+
54
+ Catch common failures from `base_api`: `DownloadFailed`, `NetworkError`,
55
+ `NotFound`, `ProxyError`, `BotDetection`, `VideoUnavailable`, `LoginFailed`, and
56
+ `RegionBlocked`. Existing provider exception imports remain compatible,
57
+ including Pornhub's `PornhubAPIError` hierarchy. Translated exceptions preserve
58
+ the original exception as `__cause__`.
59
+
60
+ Ordinary download failures now raise `DownloadFailed` instead of returning
61
+ `False`. A stop signal raises `base_api.modules.errors.DownloadCancelled`;
62
+ async task cancellation propagates unchanged. Neither is logged as an error.
63
+ With `DownloadConfigHLS(return_report=True)`, incomplete/cancelled downloads
64
+ still return their explicit `DownloadReport`; unexpected exceptions still raise.
65
+
66
+ Shared request translation, download error handling, HLS preparation, and output
67
+ configuration live in `base_api.modules.provider`. The identical Tube8 and
68
+ Thumbzilla result-grid extraction lives in `base_api.modules.static_functions`.
69
+ Site-specific parsing, quality selection, and fallback behavior stay in each
70
+ provider. Updated providers require `eaf-base-api>=4.2.0`; update the base library
71
+ alongside them.
72
+
73
+ # Documentation (IMPORTANT!)
18
74
  > [!IMPORTANT]
19
75
  > Configuring eaf_base_api is necessary if you use any of my Porn APIs, because they all depend on this project.
20
76
  > Please read through the documentation to learn how `PROXIES`, `CACHING` and `LOGGING` etc... work!
@@ -134,7 +190,10 @@ stream = helper.iterator(
134
190
  async with stream:
135
191
  async for result in stream:
136
192
  if not result.succeeded:
137
- logger.error("%s failed: %s", result.stage, result.error)
193
+ logger.error(
194
+ "%s failed for %s: %s", result.stage, result.url, result.error,
195
+ exc_info=(type(result.error), result.error, result.error.__traceback__),
196
+ )
138
197
  continue
139
198
  video = result.unwrap()
140
199
  ```
@@ -145,6 +204,31 @@ or `ErrorMode.RAISE`. `RetryPolicy` provides a strict maximum attempt count and
145
204
  optional exponential delay; the independent page and item handlers return an
146
205
  `ErrorAction` and cannot create an unbounded retry loop.
147
206
 
207
+ ## Error logging
208
+
209
+ Configure logging once in the application to capture all provider and base API logs:
210
+
211
+ ```python
212
+ import logging
213
+ from base_api.modules.logger import configure_app_logging
214
+
215
+ configure_app_logging(log_file="api.log", level=logging.INFO)
216
+ ```
217
+
218
+ Failures include the operation, video/page URL, and original traceback. The default
219
+ formatter also shows the file, line, and function. Media source failures are logged
220
+ even when a caller catches the exception; iterator failures are logged even when
221
+ the configured policy skips or yields them. Provider CLIs configure console logging
222
+ automatically.
223
+
224
+ Provider request and download exceptions use the shared types in
225
+ `base_api.modules.errors`, preserving the original exception as `__cause__`.
226
+ Pornhub's existing exception classes remain catchable as `PornhubAPIError` and as
227
+ their shared equivalents. `ScraperException` also inherits `BaseScraperError`.
228
+ Download preparation errors now carry the video URL in `DownloadFailed`, and
229
+ explicit cancellation remains `DownloadCancelled` or `asyncio.CancelledError`.
230
+ Existing boolean/download-report results from the base downloader remain supported.
231
+
148
232
  # Can I use this for myself?
149
233
  Yes, you can, but I may change stuff here and there from time to time, and it would maybe break your project.
150
234
  I would not recommend you to install and use it as a package, but just copy the code you need.
@@ -15,6 +15,7 @@ __all__ = [
15
15
  "FieldNotLoadableError",
16
16
  "Helper",
17
17
  "ItemFetchError",
18
+ "IteratorConfig",
18
19
  "LoadState",
19
20
  "LoaderConfigurationError",
20
21
  "LoaderContractError",
@@ -35,6 +36,30 @@ __all__ = [
35
36
  "errors",
36
37
  "media_field",
37
38
  "UnknownMediaFieldError",
39
+ "make_iterator_config",
40
+ "default_on_error",
41
+ "scrape_stream",
42
+ "stream_results",
43
+ "is_resource_gone",
44
+ "contains_resource_gone",
45
+ "parse_duration",
46
+ "parse_count",
47
+ "get_text_safe",
48
+ "get_attr_safe",
49
+ "build_m3u8_master",
50
+ "str_to_bool",
51
+ "ScraperException",
52
+ "NotFound",
53
+ "NetworkError",
54
+ "BotDetection",
55
+ "ProxyError",
56
+ "UnknownNetworkError",
57
+ "DownloadFailed",
58
+ "VideoUnavailable",
59
+ "ResourceGone",
60
+ "LoginFailed",
61
+ "RegionBlocked",
62
+ "configure_app_logging",
38
63
  ]
39
64
 
40
65
 
@@ -53,7 +78,21 @@ from base_api.modules.errors import (
53
78
  RequestRetriesExhausted,
54
79
  ScrapeOperationError,
55
80
  UnknownMediaFieldError,
81
+ is_resource_gone,
82
+ contains_resource_gone,
83
+ ScraperException,
84
+ NotFound,
85
+ NetworkError,
86
+ BotDetection,
87
+ ProxyError,
88
+ UnknownNetworkError,
89
+ DownloadFailed,
90
+ VideoUnavailable,
91
+ ResourceGone,
92
+ LoginFailed,
93
+ RegionBlocked,
56
94
  )
95
+ from base_api.modules.logger import configure_app_logging
57
96
  from base_api.base import (
58
97
  BaseCore,
59
98
  BaseMedia,
@@ -74,5 +113,22 @@ from base_api.base import (
74
113
  ScrapeStream,
75
114
  SegmentCacheKey,
76
115
  media_field,
116
+ scrape_stream,
117
+ stream_results,
118
+ )
119
+ from base_api.modules.config import (
120
+ config,
121
+ DownloadConfigHLS,
122
+ DownloadConfigRAW,
123
+ IteratorConfig,
124
+ make_iterator_config,
125
+ default_on_error,
126
+ )
127
+ from base_api.modules.static_functions import (
128
+ str_to_bool,
129
+ parse_duration,
130
+ parse_count,
131
+ get_text_safe,
132
+ get_attr_safe,
133
+ build_m3u8_master,
77
134
  )
78
- from base_api.modules.config import config, DownloadConfigHLS, DownloadConfigRAW