Add new api endpoint to generate yt-dlp commands
This commit is contained in:
parent
b5683a3e77
commit
444dc72e8f
2 changed files with 62 additions and 79 deletions
80
API.md
80
API.md
|
|
@ -18,6 +18,7 @@ This document describes the available endpoints and their usage. All endpoints r
|
||||||
- [Endpoints](#endpoints)
|
- [Endpoints](#endpoints)
|
||||||
- [GET /api/ping](#get-apiping)
|
- [GET /api/ping](#get-apiping)
|
||||||
- [POST /api/yt-dlp/convert](#post-apiyt-dlpconvert)
|
- [POST /api/yt-dlp/convert](#post-apiyt-dlpconvert)
|
||||||
|
- [POST /api/yt-dlp/command/](#post-apiyt-dlpcommand)
|
||||||
- [GET /api/yt-dlp/url/info](#get-apiyt-dlpurlinfo)
|
- [GET /api/yt-dlp/url/info](#get-apiyt-dlpurlinfo)
|
||||||
- [GET /api/history/add](#get-apihistoryadd)
|
- [GET /api/history/add](#get-apihistoryadd)
|
||||||
- [POST /api/history](#post-apihistory)
|
- [POST /api/history](#post-apihistory)
|
||||||
|
|
@ -64,7 +65,6 @@ This document describes the available endpoints and their usage. All endpoints r
|
||||||
- [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/yt-dlp/archive\_id/](#post-apiyt-dlparchive_id)
|
||||||
- [POST /api/yt-dlp/save\_cookies/](#post-apiyt-dlpsave_cookies)
|
|
||||||
- [POST /api/notifications/test](#post-apinotificationstest)
|
- [POST /api/notifications/test](#post-apinotificationstest)
|
||||||
- [GET /api/yt-dlp/options](#get-apiyt-dlpoptions)
|
- [GET /api/yt-dlp/options](#get-apiyt-dlpoptions)
|
||||||
- [POST /api/system/pause](#post-apisystempause)
|
- [POST /api/system/pause](#post-apisystempause)
|
||||||
|
|
@ -210,6 +210,45 @@ or an error:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### POST /api/yt-dlp/command/
|
||||||
|
**Purpose**: Build a complete yt-dlp CLI command string with priority-based argument merging from user input, presets, and defaults.
|
||||||
|
|
||||||
|
**Requires**: Console must be enabled (`YTP_CONSOLE_ENABLED=true` env).
|
||||||
|
|
||||||
|
**Body**: JSON object:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"url": "https://example.com/video",// required - item url
|
||||||
|
"preset": "preset_name", // optional - preset name to apply
|
||||||
|
"folder": "subfolder", // optional - output folder (relative to download_path)
|
||||||
|
"template": "%(title)s.%(ext)s", // optional - output filename template
|
||||||
|
"cli": "--write-sub --embed-subs", // optional - additional yt-dlp CLI arguments
|
||||||
|
"cookies": "cookie_string" // optional - authentication cookies as string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If cookies are given, they will be stored in a temporary file and the appropriate `--cookies <file>` argument
|
||||||
|
will be added to the command.
|
||||||
|
|
||||||
|
**Priority System** (User > Preset > Default):
|
||||||
|
1. **User fields** take highest priority (from request body)
|
||||||
|
2. **Preset fields** used only if user didn't provide them
|
||||||
|
3. **Default fields** used as final fallback (from configuration)
|
||||||
|
|
||||||
|
**Response**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "--output-path /downloads/subfolder --output %(title)s.%(ext)s --write-sub --embed-subs https://example.com/video"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Error Responses**:
|
||||||
|
- `403 Forbidden` if console is disabled
|
||||||
|
- `400 Bad Request` if body is invalid JSON or Item format validation fails
|
||||||
|
- `400 Bad Request` if CLI command building fails
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### GET /api/yt-dlp/url/info
|
### GET /api/yt-dlp/url/info
|
||||||
**Purpose**: Retrieves metadata (info) for a provided URL without adding it to the download queue.
|
**Purpose**: Retrieves metadata (info) for a provided URL without adding it to the download queue.
|
||||||
|
|
||||||
|
|
@ -1450,45 +1489,6 @@ or an error:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### POST /api/yt-dlp/save_cookies/
|
|
||||||
**Purpose**: Save cookies to a file for use with yt-dlp CLI operations. Requires console to be enabled (`console_enabled` in configuration).
|
|
||||||
**Body**: JSON object with `cookies` field containing the cookie string.
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"cookies": "cookie_string_or_netscape_format"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response on Success**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"status": true,
|
|
||||||
"cookie_file": "/path/to/temp/c_uuid.txt"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response on Error**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"error": "error_message"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Status Codes**:
|
|
||||||
- `200 OK` if cookies were successfully saved.
|
|
||||||
- `400 Bad Request` if the request body is invalid or missing the `cookies` field.
|
|
||||||
- `403 Forbidden` if console is disabled.
|
|
||||||
- `413 Payload Too Large` if cookies exceed 1MB.
|
|
||||||
- `500 Internal Server Error` if cookie file creation fails.
|
|
||||||
|
|
||||||
**Notes**:
|
|
||||||
- Console must be enabled in configuration (`console_enabled: true`).
|
|
||||||
- Cookies must be a valid string (≤ 1MB).
|
|
||||||
- Cookies are stored in the temporary directory with a UUID-based filename.
|
|
||||||
- Cookie files can be used with subsequent yt-dlp operations.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### POST /api/notifications/test
|
### POST /api/notifications/test
|
||||||
**Purpose**: Triggers a test notification event to all configured targets.
|
**Purpose**: Triggers a test notification event to all configured targets.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from pathlib import Path
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
|
|
@ -10,6 +9,7 @@ from aiohttp.web import Request, Response
|
||||||
|
|
||||||
from app.library.cache import Cache
|
from app.library.cache import Cache
|
||||||
from app.library.config import Config
|
from app.library.config import Config
|
||||||
|
from app.library.ItemDTO import Item
|
||||||
from app.library.Presets import Presets
|
from app.library.Presets import Presets
|
||||||
from app.library.router import route
|
from app.library.router import route
|
||||||
from app.library.Utils import (
|
from app.library.Utils import (
|
||||||
|
|
@ -20,7 +20,7 @@ from app.library.Utils import (
|
||||||
get_archive_id,
|
get_archive_id,
|
||||||
validate_url,
|
validate_url,
|
||||||
)
|
)
|
||||||
from app.library.YTDLPOpts import YTDLPOpts
|
from app.library.YTDLPOpts import YTDLPCli, YTDLPOpts
|
||||||
|
|
||||||
LOG: logging.Logger = logging.getLogger(__name__)
|
LOG: logging.Logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -275,58 +275,41 @@ async def get_archive_ids(request: Request, config: Config) -> Response:
|
||||||
return web.json_response(data=response, status=web.HTTPOk.status_code)
|
return web.json_response(data=response, status=web.HTTPOk.status_code)
|
||||||
|
|
||||||
|
|
||||||
@route("POST", "api/yt-dlp/save_cookies/", "save_cookies")
|
@route("POST", "api/yt-dlp/command/", "make_command")
|
||||||
async def save_cookies(request: Request, config: Config) -> Response:
|
async def make_command(request: Request, config: Config) -> Response:
|
||||||
"""
|
"""
|
||||||
Save cookies for use with CLI.
|
Build yt-dlp CLI command.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (Request): The request object.
|
||||||
|
config (Config): The config instance.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Response: The response object with the yt-dlp CLI options.
|
Response: The response object with the merged fields and final yt-dlp CLI command string.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not config.console_enabled:
|
if not config.console_enabled:
|
||||||
return web.json_response(
|
return web.json_response(data={"error": "Console is disabled."}, status=web.HTTPForbidden.status_code)
|
||||||
data={"error": "Console is disabled."},
|
|
||||||
status=web.HTTPForbidden.status_code,
|
|
||||||
)
|
|
||||||
|
|
||||||
data = (await request.json()) if request.body_exists else None
|
data = (await request.json()) if request.body_exists else None
|
||||||
if not data or not isinstance(data, dict):
|
if not data or not isinstance(data, dict):
|
||||||
return web.json_response(
|
return web.json_response(
|
||||||
data={"error": "Invalid request. expecting dict with 'cookies' field."},
|
data={"error": "Invalid request. expecting JSON body."},
|
||||||
status=web.HTTPBadRequest.status_code,
|
status=web.HTTPBadRequest.status_code,
|
||||||
)
|
)
|
||||||
|
|
||||||
cookies = data.get("cookies")
|
|
||||||
if not cookies or not isinstance(cookies, str):
|
|
||||||
return web.json_response(
|
|
||||||
data={"error": "Invalid request. 'cookies' field is required and must be a string."},
|
|
||||||
status=web.HTTPBadRequest.status_code,
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(cookies) > 1_000_000:
|
|
||||||
return web.json_response(
|
|
||||||
data={"error": "Cookie size exceeds the maximum limit of 1MB."},
|
|
||||||
status=web.HTTPBadRequest.status_code,
|
|
||||||
)
|
|
||||||
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from app.library.Utils import load_cookies
|
|
||||||
|
|
||||||
cookie_file: Path = Path(config.temp_path) / f"c_{uuid.uuid4()!s}.txt"
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cookie_file.write_text(cookies)
|
it = Item.format(data)
|
||||||
file_path = str(cookie_file)
|
except ValueError as e:
|
||||||
load_cookies(cookie_file)
|
return web.json_response(data={"error": str(e), "data": data}, status=web.HTTPBadRequest.status_code)
|
||||||
|
|
||||||
|
try:
|
||||||
|
command, _ = YTDLPCli(item=it, config=config).build()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
LOG.exception(e)
|
||||||
return web.json_response(
|
return web.json_response(
|
||||||
data={"error": f"Failed to create cookie file. '{e!s}'."},
|
data={"error": "Failed to build CLI command"},
|
||||||
status=web.HTTPInternalServerError.status_code,
|
status=web.HTTPBadRequest.status_code,
|
||||||
)
|
)
|
||||||
|
|
||||||
return web.json_response(
|
return web.json_response(data={"command": command}, status=web.HTTPOk.status_code)
|
||||||
data={"status": True, "cookie_file": file_path},
|
|
||||||
status=web.HTTPOk.status_code,
|
|
||||||
)
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue