added new endpoint to get urls archive ids
This commit is contained in:
parent
21c7b0f208
commit
71bd40a2e8
3 changed files with 104 additions and 12 deletions
55
API.md
55
API.md
|
|
@ -44,7 +44,9 @@ This document describes the available endpoints and their usage. All endpoints r
|
|||
- [GET /api/logs](#get-apilogs)
|
||||
- [GET /api/notifications](#get-apinotifications)
|
||||
- [PUT /api/notifications](#put-apinotifications)
|
||||
- [POST /api/yt-dlp/archive\_id/](#post-apiyt-dlparchive_id)
|
||||
- [POST /api/notifications/test](#post-apinotificationstest)
|
||||
- [GET /api/yt-dlp/options](#get-apiyt-dlpoptions)
|
||||
- [Error Responses](#error-responses)
|
||||
|
||||
---
|
||||
|
|
@ -824,6 +826,41 @@ Binary image data with appropriate `Content-Type` header.
|
|||
"allowedTypes": ["added", "completed", "error", "cancelled", "cleared", "log_info", "log_success", ...]
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
### POST /api/yt-dlp/archive_id/
|
||||
**Purpose**: Get the archive ID for a given URLs.
|
||||
**Body**: Array of URLs.
|
||||
```json
|
||||
[
|
||||
"https://youtube.com/...",
|
||||
"https://..."
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"index": "index_of_the_url_in_request_array",
|
||||
"url": "the_url",
|
||||
"id": "the_video_id_or_null_if_not_found",
|
||||
"ie_key": "the_extractor_key_or_null_if_not_found",
|
||||
"archive_id": "the_archive_id_or_null_if_not_found",
|
||||
"error": "error_message_if_any_or_null"
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
or an error:
|
||||
```json
|
||||
{
|
||||
"error": "text"
|
||||
}
|
||||
```
|
||||
|
||||
- If the body is not a valid JSON array, returns `400 Bad Request`.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -840,6 +877,24 @@ Binary image data with appropriate `Content-Type` header.
|
|||
|
||||
---
|
||||
|
||||
### GET /api/yt-dlp/options
|
||||
**Purpose**: Get the current yt-dlp CLI options as a JSON object.
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"description": "Description of the option",
|
||||
"flags":[ "--option", "-o" ],
|
||||
"group": "Option Group",
|
||||
"ignored": false, // true if this option is ignored by ytptube.
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Responses
|
||||
|
||||
Most endpoints return standard error codes (`400`, `403`, `404`, `500`, etc.) and a JSON body on failure. For example:
|
||||
|
|
|
|||
|
|
@ -405,13 +405,9 @@ def check_id(file: Path) -> bool | str:
|
|||
|
||||
@lru_cache(maxsize=512)
|
||||
def is_private_address(hostname: str) -> bool:
|
||||
try:
|
||||
ip = socket.gethostbyname(hostname)
|
||||
ip_obj = ipaddress.ip_address(ip)
|
||||
return ip_obj.is_private or ip_obj.is_loopback or ip_obj.is_reserved or ip_obj.is_link_local
|
||||
except socket.gaierror:
|
||||
# Could not resolve - treat as invalid or restricted
|
||||
return True
|
||||
ip = socket.gethostbyname(hostname)
|
||||
ip_obj = ipaddress.ip_address(ip)
|
||||
return ip_obj.is_private or ip_obj.is_loopback or ip_obj.is_reserved or ip_obj.is_link_local
|
||||
|
||||
|
||||
def validate_url(url: str, allow_internal: bool = False) -> bool:
|
||||
|
|
@ -447,10 +443,20 @@ def validate_url(url: str, allow_internal: bool = False) -> bool:
|
|||
raise ValueError(msg)
|
||||
|
||||
hostname: str | None = parsed_url.host
|
||||
if allow_internal is False and (not hostname or is_private_address(hostname)):
|
||||
msg = "Access to internal urls or private networks is not allowed."
|
||||
if not hostname:
|
||||
msg = "Invalid hostname."
|
||||
raise ValueError(msg)
|
||||
|
||||
if allow_internal is False:
|
||||
try:
|
||||
if is_private_address(hostname):
|
||||
msg = "Access to internal urls or private networks is not allowed."
|
||||
raise ValueError(msg)
|
||||
except socket.gaierror as e:
|
||||
LOG.error(f"Error resolving hostname '{hostname}': {e!s}")
|
||||
msg = "Invalid hostname."
|
||||
raise ValueError(msg) from e
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -309,6 +309,37 @@ async def get_options() -> Response:
|
|||
"""
|
||||
from app.library.ytdlp import ytdlp_options
|
||||
|
||||
return web.json_response(
|
||||
body=json.dumps(ytdlp_options(), indent=4, default=str), status=web.HTTPOk.status_code
|
||||
)
|
||||
return web.json_response(body=json.dumps(ytdlp_options(), indent=4, default=str), status=web.HTTPOk.status_code)
|
||||
|
||||
|
||||
@route("POST", "api/yt-dlp/archive_id/", "get_archive_ids")
|
||||
async def get_archive_ids(request: Request) -> Response:
|
||||
"""
|
||||
Get the yt-dlp CLI options.
|
||||
|
||||
Returns:
|
||||
Response: The response object with the yt-dlp CLI options.
|
||||
|
||||
"""
|
||||
from app.library.Utils import get_archive_id
|
||||
|
||||
data = (await request.json()) if request.body_exists else None
|
||||
if not data or not isinstance(data, list):
|
||||
return web.json_response(
|
||||
data={"error": "Invalid request. expecting list with URLs."},
|
||||
status=web.HTTPBadRequest.status_code,
|
||||
)
|
||||
|
||||
response = []
|
||||
|
||||
for i, url in enumerate(data):
|
||||
dct = {"index": i, "url": url}
|
||||
try:
|
||||
validate_url(url)
|
||||
dct.update(get_archive_id(url))
|
||||
except ValueError as e:
|
||||
dct.update({"id": None, "ie_key": None, "archive_id": None, "error": str(e)})
|
||||
|
||||
response.append(dct)
|
||||
|
||||
return web.json_response(data=response, status=web.HTTPOk.status_code)
|
||||
|
|
|
|||
Loading…
Reference in a new issue