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/logs](#get-apilogs)
|
||||||
- [GET /api/notifications](#get-apinotifications)
|
- [GET /api/notifications](#get-apinotifications)
|
||||||
- [PUT /api/notifications](#put-apinotifications)
|
- [PUT /api/notifications](#put-apinotifications)
|
||||||
|
- [POST /api/yt-dlp/archive\_id/](#post-apiyt-dlparchive_id)
|
||||||
- [POST /api/notifications/test](#post-apinotificationstest)
|
- [POST /api/notifications/test](#post-apinotificationstest)
|
||||||
|
- [GET /api/yt-dlp/options](#get-apiyt-dlpoptions)
|
||||||
- [Error Responses](#error-responses)
|
- [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", ...]
|
"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
|
## Error Responses
|
||||||
|
|
||||||
Most endpoints return standard error codes (`400`, `403`, `404`, `500`, etc.) and a JSON body on failure. For example:
|
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)
|
@lru_cache(maxsize=512)
|
||||||
def is_private_address(hostname: str) -> bool:
|
def is_private_address(hostname: str) -> bool:
|
||||||
try:
|
ip = socket.gethostbyname(hostname)
|
||||||
ip = socket.gethostbyname(hostname)
|
ip_obj = ipaddress.ip_address(ip)
|
||||||
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
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def validate_url(url: str, allow_internal: bool = False) -> bool:
|
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)
|
raise ValueError(msg)
|
||||||
|
|
||||||
hostname: str | None = parsed_url.host
|
hostname: str | None = parsed_url.host
|
||||||
if allow_internal is False and (not hostname or is_private_address(hostname)):
|
if not hostname:
|
||||||
msg = "Access to internal urls or private networks is not allowed."
|
msg = "Invalid hostname."
|
||||||
raise ValueError(msg)
|
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
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -309,6 +309,37 @@ async def get_options() -> Response:
|
||||||
"""
|
"""
|
||||||
from app.library.ytdlp import ytdlp_options
|
from app.library.ytdlp import ytdlp_options
|
||||||
|
|
||||||
return web.json_response(
|
return web.json_response(body=json.dumps(ytdlp_options(), indent=4, default=str), status=web.HTTPOk.status_code)
|
||||||
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