Add show color and notes params
This commit is contained in:
parent
952440bb05
commit
7f7c10d02c
5 changed files with 35 additions and 2 deletions
|
|
@ -181,6 +181,7 @@ Show endpoints manage scheduled playlist runs. They support bot Bearer auth and
|
||||||
|
|
||||||
```python
|
```python
|
||||||
shows = bot.list_shows() # rank >= MOD
|
shows = bot.list_shows() # rank >= MOD
|
||||||
|
public_shows = bot.list_public_shows() # read-only public schedule
|
||||||
show = bot.get_show(show_id) # rank >= MOD
|
show = bot.get_show(show_id) # rank >= MOD
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
|
|
@ -195,6 +196,8 @@ payload = {
|
||||||
{"type": "yt", "id": "dQw4w9WgXcQ", "pos": "end"}
|
{"type": "yt", "id": "dQw4w9WgXcQ", "pos": "end"}
|
||||||
],
|
],
|
||||||
"status": "scheduled",
|
"status": "scheduled",
|
||||||
|
"notes": "<p>Special guest this week.</p>",
|
||||||
|
"color": "#22AAEE",
|
||||||
}
|
}
|
||||||
|
|
||||||
created = bot.create_show(payload) # rank >= MOD
|
created = bot.create_show(payload) # rank >= MOD
|
||||||
|
|
@ -215,6 +218,8 @@ Create/update payload constraints:
|
||||||
- `conflict_mode`: `force` | `skip`
|
- `conflict_mode`: `force` | `skip`
|
||||||
- `playlist`: required non-empty array of media entries with `type`, `id`, and optional `pos` (`next` | `end`)
|
- `playlist`: required non-empty array of media entries with `type`, `id`, and optional `pos` (`next` | `end`)
|
||||||
- `status`: `draft` | `scheduled` | `paused` | `completed` | `failed` | `canceled` (`running` is internal)
|
- `status`: `draft` | `scheduled` | `paused` | `completed` | `failed` | `canceled` (`running` is internal)
|
||||||
|
- `notes`: optional `string | null` (rich HTML allowed; expected max input length 20000 chars before sanitize)
|
||||||
|
- `color`: optional `string | null`, must match `^#[0-9A-Fa-f]{6}$` when provided
|
||||||
|
|
||||||
Action payload schema:
|
Action payload schema:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "veretube-bot"
|
name = "veretube-bot"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
description = "Python bot library for veretube sync channels"
|
description = "Python bot library for veretube/sync v4.0 channels"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
import re
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
@ -122,6 +123,8 @@ class BotAPI:
|
||||||
|
|
||||||
# ── Shows ─────────────────────────────────────────────────────────────────
|
# ── Shows ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
_SHOW_COLOR_RE = re.compile(r"^#[0-9A-Fa-f]{6}$")
|
||||||
|
|
||||||
def _validate_show_payload(self, payload: dict) -> None:
|
def _validate_show_payload(self, payload: dict) -> None:
|
||||||
timezone = payload.get("timezone")
|
timezone = payload.get("timezone")
|
||||||
if not isinstance(timezone, str) or not timezone.strip():
|
if not isinstance(timezone, str) or not timezone.strip():
|
||||||
|
|
@ -143,6 +146,20 @@ class BotAPI:
|
||||||
if status is not None and status not in {"draft", "scheduled", "paused", "completed", "failed", "canceled"}:
|
if status is not None and status not in {"draft", "scheduled", "paused", "completed", "failed", "canceled"}:
|
||||||
raise ValueError("status must be one of: draft, scheduled, paused, completed, failed, canceled")
|
raise ValueError("status must be one of: draft, scheduled, paused, completed, failed, canceled")
|
||||||
|
|
||||||
|
notes = payload.get("notes")
|
||||||
|
if notes is not None:
|
||||||
|
if not isinstance(notes, str):
|
||||||
|
raise ValueError("notes must be a string or null")
|
||||||
|
if len(notes) > 20000:
|
||||||
|
raise ValueError("notes must be at most 20000 characters")
|
||||||
|
|
||||||
|
color = payload.get("color")
|
||||||
|
if color is not None:
|
||||||
|
if not isinstance(color, str):
|
||||||
|
raise ValueError("color must be a string or null")
|
||||||
|
if not self._SHOW_COLOR_RE.fullmatch(color):
|
||||||
|
raise ValueError("color must match /^#[0-9A-Fa-f]{6}$/")
|
||||||
|
|
||||||
playlist = payload.get("playlist")
|
playlist = payload.get("playlist")
|
||||||
if not isinstance(playlist, list) or not playlist:
|
if not isinstance(playlist, list) or not playlist:
|
||||||
raise ValueError("playlist is required and must be a non-empty list")
|
raise ValueError("playlist is required and must be a non-empty list")
|
||||||
|
|
@ -159,6 +176,10 @@ class BotAPI:
|
||||||
"""List channel shows. Requires rank >= MOD."""
|
"""List channel shows. Requires rank >= MOD."""
|
||||||
return self._request("GET", "/shows")
|
return self._request("GET", "/shows")
|
||||||
|
|
||||||
|
def list_public_shows(self) -> list:
|
||||||
|
"""List public show schedule. Read-only."""
|
||||||
|
return self._request("GET", "/shows/public")
|
||||||
|
|
||||||
def get_show(self, show_id: int | str) -> dict:
|
def get_show(self, show_id: int | str) -> dict:
|
||||||
"""Get a single show by id. Requires rank >= MOD."""
|
"""Get a single show by id. Requires rank >= MOD."""
|
||||||
return self._request("GET", f"/shows/{show_id}")
|
return self._request("GET", f"/shows/{show_id}")
|
||||||
|
|
|
||||||
|
|
@ -250,6 +250,9 @@ class AsyncBot:
|
||||||
async def list_shows(self) -> list:
|
async def list_shows(self) -> list:
|
||||||
return await asyncio.to_thread(self.api.list_shows)
|
return await asyncio.to_thread(self.api.list_shows)
|
||||||
|
|
||||||
|
async def list_public_shows(self) -> list:
|
||||||
|
return await asyncio.to_thread(self.api.list_public_shows)
|
||||||
|
|
||||||
async def get_show(self, show_id: int | str) -> dict:
|
async def get_show(self, show_id: int | str) -> dict:
|
||||||
return await asyncio.to_thread(self.api.get_show, show_id)
|
return await asyncio.to_thread(self.api.get_show, show_id)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -330,6 +330,10 @@ class Bot:
|
||||||
"""List scheduled shows. Requires rank >= MOD."""
|
"""List scheduled shows. Requires rank >= MOD."""
|
||||||
return self.api.list_shows()
|
return self.api.list_shows()
|
||||||
|
|
||||||
|
def list_public_shows(self) -> list:
|
||||||
|
"""List public scheduled shows. Read-only."""
|
||||||
|
return self.api.list_public_shows()
|
||||||
|
|
||||||
def get_show(self, show_id: int | str) -> dict:
|
def get_show(self, show_id: int | str) -> dict:
|
||||||
"""Get a single show by id. Requires rank >= MOD."""
|
"""Get a single show by id. Requires rank >= MOD."""
|
||||||
return self.api.get_show(show_id)
|
return self.api.get_show(show_id)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue