Added the socket API to API.md
This commit is contained in:
parent
716bbbc6fb
commit
7512fde77b
1 changed files with 653 additions and 0 deletions
653
API.md
653
API.md
|
|
@ -72,6 +72,51 @@ This document describes the available endpoints and their usage. All endpoints r
|
||||||
- [GET /api/dev/loop](#get-apidevloop)
|
- [GET /api/dev/loop](#get-apidevloop)
|
||||||
- [GET /api/dev/pip](#get-apidevpip)
|
- [GET /api/dev/pip](#get-apidevpip)
|
||||||
- [GET /api/docs/{file}](#get-apidocsfile)
|
- [GET /api/docs/{file}](#get-apidocsfile)
|
||||||
|
- [WebSocket API](#websocket-api)
|
||||||
|
- [Connection](#connection)
|
||||||
|
- [Authentication](#authentication-1)
|
||||||
|
- [Message Format](#message-format)
|
||||||
|
- [Core Events](#core-events)
|
||||||
|
- [Connection Events](#connection-events)
|
||||||
|
- [`connect` (Built-in)](#connect-built-in)
|
||||||
|
- [`disconnect` (Built-in)](#disconnect-built-in)
|
||||||
|
- [`connected` (Server → Client)](#connected-server--client)
|
||||||
|
- [Subscription Events](#subscription-events)
|
||||||
|
- [`subscribe` (Client → Server)](#subscribe-client--server)
|
||||||
|
- [`unsubscribe` (Client → Server)](#unsubscribe-client--server)
|
||||||
|
- [`subscribed` (Server → Client)](#subscribed-server--client)
|
||||||
|
- [`unsubscribed` (Server → Client)](#unsubscribed-server--client)
|
||||||
|
- [Logging Events](#logging-events)
|
||||||
|
- [`log_info` (Server → Client)](#log_info-server--client)
|
||||||
|
- [`log_success` (Server → Client)](#log_success-server--client)
|
||||||
|
- [`log_warning` (Server → Client)](#log_warning-server--client)
|
||||||
|
- [`log_error` (Server → Client)](#log_error-server--client)
|
||||||
|
- [`log_lines` (Server → Client)](#log_lines-server--client)
|
||||||
|
- [Download Queue Events](#download-queue-events)
|
||||||
|
- [`add_url` (Client → Server)](#add_url-client--server)
|
||||||
|
- [`item_added` (Server → Client)](#item_added-server--client)
|
||||||
|
- [`item_updated` (Server → Client)](#item_updated-server--client)
|
||||||
|
- [`item_completed` (Server → Client)](#item_completed-server--client)
|
||||||
|
- [`item_cancelled` (Server → Client)](#item_cancelled-server--client)
|
||||||
|
- [`item_deleted` (Server → Client)](#item_deleted-server--client)
|
||||||
|
- [`item_cancel` (Client → Server)](#item_cancel-client--server)
|
||||||
|
- [`item_delete` (Client → Server)](#item_delete-client--server)
|
||||||
|
- [`item_start` (Client → Server)](#item_start-client--server)
|
||||||
|
- [`item_pause` (Client → Server)](#item_pause-client--server)
|
||||||
|
- [`item_moved` (Server → Client)](#item_moved-server--client)
|
||||||
|
- [Queue Control Events](#queue-control-events)
|
||||||
|
- [`paused` (Server → Client)](#paused-server--client)
|
||||||
|
- [`resumed` (Server → Client)](#resumed-server--client)
|
||||||
|
- [Terminal/CLI Events](#terminalcli-events)
|
||||||
|
- [`cli_post` (Client → Server)](#cli_post-client--server)
|
||||||
|
- [`cli_output` (Server → Client)](#cli_output-server--client)
|
||||||
|
- [`cli_close` (Server → Client)](#cli_close-server--client)
|
||||||
|
- [Configuration Events](#configuration-events)
|
||||||
|
- [`presets_update` (Server → Client)](#presets_update-server--client)
|
||||||
|
- [`dlfields_update` (Server → Client)](#dlfields_update-server--client)
|
||||||
|
- [Client Implementation Examples](#client-implementation-examples)
|
||||||
|
- [Vue 3 Composable Pattern](#vue-3-composable-pattern)
|
||||||
|
- [Python Client Example](#python-client-example)
|
||||||
- [Error Responses](#error-responses)
|
- [Error Responses](#error-responses)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -1517,6 +1562,614 @@ or an error:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## WebSocket API
|
||||||
|
|
||||||
|
The WebSocket API provides real-time bidirectional communication between the client and server using Socket.IO protocol. It enables live updates for downloads, queue status, notifications, and terminal access.
|
||||||
|
|
||||||
|
### Connection
|
||||||
|
|
||||||
|
**URL**: `ws://localhost:8081/socket.io/` (development) or `https://yourdomain.com/socket.io/` (production)
|
||||||
|
|
||||||
|
The client automatically connects to the WebSocket server and receives a `connected` event with initial state. The connection uses automatic reconnection with exponential backoff (default: up to 30 attempts, 5s delay).
|
||||||
|
|
||||||
|
**Connection Options**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
transports: ['websocket', 'polling'], // Fallback to long-polling if WebSocket unavailable
|
||||||
|
withCredentials: true, // Include cookies for authentication
|
||||||
|
reconnection: true, // Enable automatic reconnection
|
||||||
|
reconnectionAttempts: 30, // Max reconnection attempts
|
||||||
|
reconnectionDelay: 5000 // Initial reconnection delay in ms
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
If Basic Authentication is configured, include credentials when establishing the WebSocket connection:
|
||||||
|
|
||||||
|
1. **Via HTTP Headers** (automatic in browsers):
|
||||||
|
```
|
||||||
|
Authorization: Basic base64("<username>:<password>")
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Via Query Parameter**:
|
||||||
|
```
|
||||||
|
ws://localhost:8081/socket.io/?apikey=<base64_urlsafe("<username>:<password>")>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Message Format
|
||||||
|
|
||||||
|
All WebSocket messages are JSON-encoded and follow a consistent structure:
|
||||||
|
|
||||||
|
**Server-to-Client (Event)** - Emitted by server, received by client:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "unique-event-id",
|
||||||
|
"created_at": "2024-01-15T10:30:00.000000+00:00",
|
||||||
|
"event": "item_added",
|
||||||
|
"title": "Item Queued",
|
||||||
|
"message": "Video added to download queue",
|
||||||
|
"data": {...}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Core Events
|
||||||
|
|
||||||
|
#### Connection Events
|
||||||
|
|
||||||
|
##### `connect` (Built-in)
|
||||||
|
Fired when WebSocket connection is established. No data payload.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('connect', () => console.log('WebSocket connected'));
|
||||||
|
```
|
||||||
|
|
||||||
|
##### `disconnect` (Built-in)
|
||||||
|
Fired when WebSocket connection is closed. No data payload.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('disconnect', (reason: string) => console.log('WebSocket disconnected:', reason));
|
||||||
|
```
|
||||||
|
|
||||||
|
##### `connected` (Server → Client)
|
||||||
|
Initial connection event with full application state.
|
||||||
|
|
||||||
|
**Data Fields**:
|
||||||
|
- `config`: Global configuration object
|
||||||
|
- `queue`: Current download queue (array of items)
|
||||||
|
- `done`: Download history (array of completed items)
|
||||||
|
- `tasks`: Scheduled tasks
|
||||||
|
- `presets`: Available download presets
|
||||||
|
- `dl_fields`: Available download fields
|
||||||
|
- `folders`: Directory structure for downloads
|
||||||
|
- `paused`: Queue pause status (boolean)
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```typescript
|
||||||
|
socket.on('connected', (data: string) => {
|
||||||
|
const json = JSON.parse(data);
|
||||||
|
const queueItems = json.data.queue || {};
|
||||||
|
const historyItems = json.data.done || {};
|
||||||
|
console.log('Connected with', Object.keys(queueItems).length, 'queued downloads');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Subscription Events
|
||||||
|
|
||||||
|
##### `subscribe` (Client → Server)
|
||||||
|
Subscribe to a specific event stream. Only supported events is `log_lines`.
|
||||||
|
|
||||||
|
**Data**: Event name as string
|
||||||
|
```javascript
|
||||||
|
socket.emit('subscribe', 'log_lines');
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response**: `subscribed` event with confirmation.
|
||||||
|
|
||||||
|
##### `unsubscribe` (Client → Server)
|
||||||
|
Unsubscribe from an event stream.
|
||||||
|
|
||||||
|
**Data**: Event name as string
|
||||||
|
```javascript
|
||||||
|
socket.emit('unsubscribe', 'log_lines');
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response**: `unsubscribed` event with confirmation.
|
||||||
|
|
||||||
|
##### `subscribed` (Server → Client)
|
||||||
|
Confirmation of successful subscription.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('subscribed', (data: string) => {
|
||||||
|
const json = JSON.parse(data);
|
||||||
|
console.log('Subscribed to event:', json.data.event);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
##### `unsubscribed` (Server → Client)
|
||||||
|
Confirmation of successful unsubscription.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('unsubscribed', (data: string) => {
|
||||||
|
const json = JSON.parse(data);
|
||||||
|
console.log('Unsubscribed from event:', json.data.event);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Logging Events
|
||||||
|
|
||||||
|
All logging events follow the same structure with JSON-encoded message:
|
||||||
|
|
||||||
|
##### `log_info` (Server → Client)
|
||||||
|
General informational message.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('log_info', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
console.info(json.message, json.data);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
##### `log_success` (Server → Client)
|
||||||
|
Success notification message.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('log_success', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
console.log('✓', json.message);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
##### `log_warning` (Server → Client)
|
||||||
|
Warning notification message.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('log_warning', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
console.warn('⚠', json.message);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
##### `log_error` (Server → Client)
|
||||||
|
Error notification message.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('log_error', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
console.error('✗', json.message);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
##### `log_lines` (Server → Client)
|
||||||
|
Continuous application log lines (requires subscription).
|
||||||
|
|
||||||
|
**Data Fields**:
|
||||||
|
- `line`: Log line content
|
||||||
|
- `timestamp`: Log timestamp
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('log_lines', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
console.log('[LOG]', json.data.line);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Download Queue Events
|
||||||
|
|
||||||
|
#### `add_url` (Client → Server)
|
||||||
|
Add a new URL to the download queue. Payload is exactly the same as
|
||||||
|
the POST `/api/queue/` endpoint.
|
||||||
|
|
||||||
|
**Data**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"url": "https://youtube.com/watch?v=...",
|
||||||
|
"preset": "default",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses**: `item_added` or `log_error` event
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.emit('add_url', {
|
||||||
|
url: 'https://youtube.com/watch?v=dQw4w9WgXcQ',
|
||||||
|
preset: 'default'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `item_added` (Server → Client)
|
||||||
|
Emitted when a new item is successfully added to the queue. The response payload is the complete item object.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('item_added', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
const item = json.data;
|
||||||
|
console.log(`Added: ${item.title} [${item.url}]`);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `item_updated` (Server → Client)
|
||||||
|
Emitted when an item's status or progress changes **(high-frequency event)**.
|
||||||
|
|
||||||
|
**Data Fields**: Same as `item_added`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('item_updated', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
const item = json.data;
|
||||||
|
console.log(`Progress: ${item.title} - ${item.progress}%`);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `item_completed` (Server → Client)
|
||||||
|
Emitted when a download completes. Item moves from queue to history.
|
||||||
|
|
||||||
|
**Data Fields**: Complete item object with final status
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('item_completed', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
const item = json.data;
|
||||||
|
console.log(`✓ Completed: ${item.title}`);
|
||||||
|
console.log(`Saved to: ${item.output_path}`);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `item_cancelled` (Server → Client)
|
||||||
|
Emitted when a download is cancelled by user.
|
||||||
|
|
||||||
|
**Data Fields**: Item object with status `cancelled`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('item_cancelled', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
const item = json.data;
|
||||||
|
console.log(`✗ Cancelled: ${item.title}`);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `item_deleted` (Server → Client)
|
||||||
|
Emitted when an item is deleted from history.
|
||||||
|
|
||||||
|
**Data Fields**: Item object
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('item_deleted', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
console.log(`Deleted from history: ${json.data._id}`);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `item_cancel` (Client → Server)
|
||||||
|
Cancel an active download.
|
||||||
|
|
||||||
|
**Data**: Item ID (string)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.emit('item_cancel', 'download-id-here');
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response**: `item_cancelled` event
|
||||||
|
|
||||||
|
#### `item_delete` (Client → Server)
|
||||||
|
Delete an item from history.
|
||||||
|
|
||||||
|
**Data**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "item-id",
|
||||||
|
"remove_file": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters**:
|
||||||
|
- `id`: Item ID (required)
|
||||||
|
- `remove_file`: Also delete downloaded file (optional)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.emit('item_delete', {
|
||||||
|
id: 'item-id',
|
||||||
|
remove_file: true // Also delete the file if deleting is enabled server-side.
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response**: `item_deleted` event
|
||||||
|
|
||||||
|
#### `item_start` (Client → Server)
|
||||||
|
Start or resume a paused download.
|
||||||
|
|
||||||
|
**Data**: Item ID (string) or array of IDs
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.emit('item_start', 'download-id');
|
||||||
|
// OR
|
||||||
|
socket.emit('item_start', ['id1', 'id2']);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response**: Queue status updated via `item_updated`
|
||||||
|
|
||||||
|
#### `item_pause` (Client → Server)
|
||||||
|
Pause an active download.
|
||||||
|
|
||||||
|
**Data**: Item ID (string) or array of IDs
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.emit('item_pause', 'download-id');
|
||||||
|
// OR
|
||||||
|
socket.emit('item_pause', ['id1', 'id2']);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response**: Queue status updated via `item_updated`
|
||||||
|
|
||||||
|
#### `item_moved` (Server → Client)
|
||||||
|
Emitted when an item moves between queue and history.
|
||||||
|
|
||||||
|
**Data Fields**:
|
||||||
|
- `to`: Destination location (`queue` or `history`)
|
||||||
|
- `item`: Complete item object
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('item_moved', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
console.log(`Item moved to: ${json.data.to}`);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Queue Control Events
|
||||||
|
|
||||||
|
#### `paused` (Server → Client)
|
||||||
|
Emitted when the entire download queue is paused.
|
||||||
|
|
||||||
|
**Data Fields**:
|
||||||
|
- `paused`: Boolean true
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('paused', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
console.log('Queue paused');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `resumed` (Server → Client)
|
||||||
|
Emitted when the download queue is resumed.
|
||||||
|
|
||||||
|
**Data Fields**:
|
||||||
|
- `paused`: Boolean false
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('resumed', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
console.log('Queue resumed');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Terminal/CLI Events
|
||||||
|
|
||||||
|
The terminal feature requires `console_enabled: true` in configuration.
|
||||||
|
|
||||||
|
#### `cli_post` (Client → Server)
|
||||||
|
Execute a command via yt-dlp CLI.
|
||||||
|
|
||||||
|
**Data**: Command arguments as string (without `yt-dlp` prefix)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.emit('cli_post', '--help');
|
||||||
|
socket.emit('cli_post', 'https://youtube.com/watch?v=... --info-json');
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses**: `cli_output` events followed by `cli_close`
|
||||||
|
|
||||||
|
#### `cli_output` (Server → Client)
|
||||||
|
Output line from the CLI command execution.
|
||||||
|
|
||||||
|
**Data Fields**:
|
||||||
|
- `type`: Output type (`stdout` or `stderr`)
|
||||||
|
- `line`: Output line content
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('cli_output', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
if (json.data.type === 'stderr') {
|
||||||
|
console.error(json.data.line);
|
||||||
|
} else {
|
||||||
|
console.log(json.data.line);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `cli_close` (Server → Client)
|
||||||
|
Emitted when CLI command execution completes.
|
||||||
|
|
||||||
|
**Data Fields**:
|
||||||
|
- `exitcode`: Command exit code (0 = success, non-zero = error)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('cli_close', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
if (json.data.exitcode === 0) {
|
||||||
|
console.log('Command completed successfully');
|
||||||
|
} else {
|
||||||
|
console.error(`Command failed with exit code ${json.data.exitcode}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration Events
|
||||||
|
|
||||||
|
#### `presets_update` (Server → Client)
|
||||||
|
Emitted when download presets are updated or created.
|
||||||
|
|
||||||
|
**Data**: Array of preset objects
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('presets_update', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
const presets = json.data || [];
|
||||||
|
console.log('Presets updated:', presets);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `dlfields_update` (Server → Client)
|
||||||
|
Emitted when download fields configuration is updated.
|
||||||
|
|
||||||
|
**Data**: Array of field objects
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
socket.on('dlfields_update', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream);
|
||||||
|
const fields = json.data || [];
|
||||||
|
console.log('Download fields updated:', fields);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Client Implementation Examples
|
||||||
|
|
||||||
|
#### Vue 3 Composable Pattern
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// composables/useSocket.ts
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import { io, type Socket } from 'socket.io-client'
|
||||||
|
|
||||||
|
export function useSocket() {
|
||||||
|
const socket = ref<Socket | null>(null)
|
||||||
|
const isConnected = ref(false)
|
||||||
|
const queueItems = ref<Record<string, any>>({})
|
||||||
|
const historyItems = ref<Record<string, any>>({})
|
||||||
|
|
||||||
|
const connect = () => {
|
||||||
|
socket.value = io('/', {
|
||||||
|
transports: ['websocket', 'polling'],
|
||||||
|
withCredentials: true,
|
||||||
|
reconnection: true,
|
||||||
|
reconnectionAttempts: 30,
|
||||||
|
reconnectionDelay: 5000
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.value.on('connect', () => {
|
||||||
|
isConnected.value = true
|
||||||
|
console.log('Connected to server')
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.value.on('connected', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream)
|
||||||
|
queueItems.value = json.data.queue || {}
|
||||||
|
historyItems.value = json.data.done || {}
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.value.on('item_added', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream)
|
||||||
|
queueItems.value[json.data._id] = json.data
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.value.on('item_updated', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream)
|
||||||
|
const item = json.data
|
||||||
|
if (queueItems.value[item._id]) {
|
||||||
|
queueItems.value[item._id] = item
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.value.on('item_completed', (stream: string) => {
|
||||||
|
const json = JSON.parse(stream)
|
||||||
|
const item = json.data
|
||||||
|
delete queueItems.value[item._id]
|
||||||
|
historyItems.value[item._id] = item
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.value.on('disconnect', () => {
|
||||||
|
isConnected.value = false
|
||||||
|
console.log('Disconnected from server')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const addDownload = (url: string, preset: string = 'default') => {
|
||||||
|
socket.value?.emit('add_url', { url, preset })
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelDownload = (id: string) => {
|
||||||
|
socket.value?.emit('item_cancel', id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
socket,
|
||||||
|
isConnected,
|
||||||
|
queueItems,
|
||||||
|
historyItems,
|
||||||
|
connect,
|
||||||
|
addDownload,
|
||||||
|
cancelDownload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Python Client Example
|
||||||
|
|
||||||
|
```python
|
||||||
|
# WebSocket client for YTPTube
|
||||||
|
import json
|
||||||
|
import asyncio
|
||||||
|
import socketio
|
||||||
|
|
||||||
|
class YTPTubeClient:
|
||||||
|
def __init__(self, url: str = 'http://localhost:8081'):
|
||||||
|
self.sio = socketio.AsyncClient(
|
||||||
|
transports=['websocket', 'polling'],
|
||||||
|
reconnection_attempts=30,
|
||||||
|
reconnection_delay=5
|
||||||
|
)
|
||||||
|
self.url = url
|
||||||
|
self.queue = {}
|
||||||
|
self.history = {}
|
||||||
|
|
||||||
|
async def connect(self):
|
||||||
|
await self.sio.connect(self.url)
|
||||||
|
await self.sio.wait()
|
||||||
|
|
||||||
|
async def setup_listeners(self):
|
||||||
|
@self.sio.on('connected')
|
||||||
|
async def on_connected(data: str):
|
||||||
|
json_data = json.loads(data)
|
||||||
|
self.queue = json_data['data'].get('queue', {})
|
||||||
|
self.history = json_data['data'].get('done', {})
|
||||||
|
print('Connected to YTPTube')
|
||||||
|
|
||||||
|
@self.sio.on('item_added')
|
||||||
|
async def on_item_added(data: str):
|
||||||
|
json_data = json.loads(data)
|
||||||
|
item = json_data['data']
|
||||||
|
self.queue[item['_id']] = item
|
||||||
|
print(f"Added: {item['title']}")
|
||||||
|
|
||||||
|
@self.sio.on('item_completed')
|
||||||
|
async def on_item_completed(data: str):
|
||||||
|
json_data = json.loads(data)
|
||||||
|
item = json_data['data']
|
||||||
|
self.queue.pop(item['_id'], None)
|
||||||
|
self.history[item['_id']] = item
|
||||||
|
print(f"Completed: {item['title']}")
|
||||||
|
|
||||||
|
async def add_download(self, url: str, preset: str = 'default'):
|
||||||
|
await self.sio.emit('add_url', {'url': url, 'preset': preset})
|
||||||
|
|
||||||
|
async def cancel_download(self, item_id: str):
|
||||||
|
await self.sio.emit('item_cancel', item_id)
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
async def main():
|
||||||
|
client = YTPTubeClient('http://localhost:8081')
|
||||||
|
await client.setup_listeners()
|
||||||
|
await client.connect()
|
||||||
|
|
||||||
|
await client.add_download('https://youtube.com/watch?v=...')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
asyncio.run(main())
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 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:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue