Add show API to bot lib
This commit is contained in:
parent
12d01f9d6d
commit
952440bb05
5 changed files with 163 additions and 1 deletions
49
README.md
49
README.md
|
|
@ -175,6 +175,53 @@ bot.clear_playlist() # clear all (rank >= ADMIN)
|
|||
|
||||
**Media types:** `yt` YouTube, `sc` SoundCloud, `tw` Twitch stream, `tc` Twitch clip, `vm` Vimeo, `dm` Dailymotion, `fi` direct file URL, `cu` custom embed.
|
||||
|
||||
## Shows
|
||||
|
||||
Show endpoints manage scheduled playlist runs. They support bot Bearer auth and session auth.
|
||||
|
||||
```python
|
||||
shows = bot.list_shows() # rank >= MOD
|
||||
show = bot.get_show(show_id) # rank >= MOD
|
||||
|
||||
payload = {
|
||||
"name": "Friday Prime",
|
||||
"scheduled_for": "2026-05-22T19:00:00.000Z",
|
||||
"timezone": "America/New_York",
|
||||
"recurrence": "weekly",
|
||||
"fill_mode": "replace",
|
||||
"conflict_mode": "force",
|
||||
"start_playback": True,
|
||||
"playlist": [
|
||||
{"type": "yt", "id": "dQw4w9WgXcQ", "pos": "end"}
|
||||
],
|
||||
"status": "scheduled",
|
||||
}
|
||||
|
||||
created = bot.create_show(payload) # rank >= MOD
|
||||
updated = bot.update_show(show_id, payload) # rank >= MOD
|
||||
bot.delete_show(show_id) # rank >= ADMIN
|
||||
|
||||
bot.show_action(show_id, "pause") # rank >= MOD
|
||||
bot.show_action(show_id, "resume") # rank >= MOD
|
||||
bot.show_action(show_id, "schedule") # rank >= MOD
|
||||
bot.show_action(show_id, "run") # rank >= ADMIN
|
||||
bot.show_action(show_id, "cancel") # rank >= ADMIN
|
||||
```
|
||||
|
||||
Create/update payload constraints:
|
||||
- `timezone`: required non-empty IANA time zone region string from the IANA Time Zone Database (for example `Europe/Berlin`, `America/New_York`)
|
||||
- `recurrence`: `none` | `daily` | `weekly`
|
||||
- `fill_mode`: `append` | `replace`
|
||||
- `conflict_mode`: `force` | `skip`
|
||||
- `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)
|
||||
|
||||
Action payload schema:
|
||||
|
||||
```json
|
||||
{ "action": "run" }
|
||||
```
|
||||
|
||||
## Moderation
|
||||
|
||||
```python
|
||||
|
|
@ -216,6 +263,8 @@ Every REST endpoint is also accessible on `bot.api` if you need something not co
|
|||
playlist = bot.api.get_playlist() # { items, currentIndex, locked }
|
||||
bot.api.skip_to(uid)
|
||||
bot.api.set_user_rank("Alice", 2)
|
||||
bot.api.list_shows()
|
||||
bot.api.show_action(show_id, "run")
|
||||
```
|
||||
|
||||
## Error handling
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||
|
||||
[project]
|
||||
name = "veretube-bot"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
description = "Python bot library for veretube sync channels"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
|
|
|
|||
|
|
@ -119,3 +119,69 @@ class BotAPI:
|
|||
def update_settings(self, **kwargs) -> None:
|
||||
"""Update one or more channel settings. Requires rank >= OWNER and channel active."""
|
||||
self._request("PUT", "/settings", json=kwargs)
|
||||
|
||||
# ── Shows ─────────────────────────────────────────────────────────────────
|
||||
|
||||
def _validate_show_payload(self, payload: dict) -> None:
|
||||
timezone = payload.get("timezone")
|
||||
if not isinstance(timezone, str) or not timezone.strip():
|
||||
raise ValueError("timezone is required and must be a non-empty IANA timezone string")
|
||||
|
||||
recurrence = payload.get("recurrence")
|
||||
if recurrence is not None and recurrence not in {"none", "daily", "weekly"}:
|
||||
raise ValueError("recurrence must be one of: none, daily, weekly")
|
||||
|
||||
fill_mode = payload.get("fill_mode")
|
||||
if fill_mode is not None and fill_mode not in {"append", "replace"}:
|
||||
raise ValueError("fill_mode must be one of: append, replace")
|
||||
|
||||
conflict_mode = payload.get("conflict_mode")
|
||||
if conflict_mode is not None and conflict_mode not in {"force", "skip"}:
|
||||
raise ValueError("conflict_mode must be one of: force, skip")
|
||||
|
||||
status = payload.get("status")
|
||||
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")
|
||||
|
||||
playlist = payload.get("playlist")
|
||||
if not isinstance(playlist, list) or not playlist:
|
||||
raise ValueError("playlist is required and must be a non-empty list")
|
||||
for index, item in enumerate(playlist):
|
||||
if not isinstance(item, dict):
|
||||
raise ValueError(f"playlist[{index}] must be an object")
|
||||
if not item.get("type") or not item.get("id"):
|
||||
raise ValueError(f"playlist[{index}] must include non-empty type and id")
|
||||
pos = item.get("pos")
|
||||
if pos is not None and pos not in {"next", "end"}:
|
||||
raise ValueError(f"playlist[{index}].pos must be one of: next, end")
|
||||
|
||||
def list_shows(self) -> list:
|
||||
"""List channel shows. Requires rank >= MOD."""
|
||||
return self._request("GET", "/shows")
|
||||
|
||||
def get_show(self, show_id: int | str) -> dict:
|
||||
"""Get a single show by id. Requires rank >= MOD."""
|
||||
return self._request("GET", f"/shows/{show_id}")
|
||||
|
||||
def create_show(self, payload: dict) -> dict:
|
||||
"""Create a show. Requires rank >= MOD."""
|
||||
self._validate_show_payload(payload)
|
||||
return self._request("POST", "/shows", json=payload)
|
||||
|
||||
def update_show(self, show_id: int | str, payload: dict) -> dict:
|
||||
"""Update a show by id. Requires rank >= MOD."""
|
||||
self._validate_show_payload(payload)
|
||||
return self._request("PUT", f"/shows/{show_id}", json=payload)
|
||||
|
||||
def delete_show(self, show_id: int | str) -> None:
|
||||
"""Delete a show by id. Requires rank >= ADMIN."""
|
||||
self._request("DELETE", f"/shows/{show_id}")
|
||||
|
||||
def show_action(self, show_id: int | str, action: str) -> dict:
|
||||
"""
|
||||
Run a show action.
|
||||
pause/resume/schedule require rank >= MOD. run/cancel require rank >= ADMIN.
|
||||
"""
|
||||
if action not in {"pause", "resume", "schedule", "run", "cancel"}:
|
||||
raise ValueError("action must be one of: pause, resume, schedule, run, cancel")
|
||||
return self._request("POST", f"/shows/{show_id}/action", json={"action": action})
|
||||
|
|
|
|||
|
|
@ -246,3 +246,21 @@ class AsyncBot:
|
|||
|
||||
async def clear_playlist(self):
|
||||
await asyncio.to_thread(self.api.clear_playlist)
|
||||
|
||||
async def list_shows(self) -> list:
|
||||
return await asyncio.to_thread(self.api.list_shows)
|
||||
|
||||
async def get_show(self, show_id: int | str) -> dict:
|
||||
return await asyncio.to_thread(self.api.get_show, show_id)
|
||||
|
||||
async def create_show(self, payload: dict) -> dict:
|
||||
return await asyncio.to_thread(self.api.create_show, payload)
|
||||
|
||||
async def update_show(self, show_id: int | str, payload: dict) -> dict:
|
||||
return await asyncio.to_thread(self.api.update_show, show_id, payload)
|
||||
|
||||
async def delete_show(self, show_id: int | str):
|
||||
await asyncio.to_thread(self.api.delete_show, show_id)
|
||||
|
||||
async def show_action(self, show_id: int | str, action: str) -> dict:
|
||||
return await asyncio.to_thread(self.api.show_action, show_id, action)
|
||||
|
|
|
|||
|
|
@ -324,6 +324,35 @@ class Bot:
|
|||
"""Clear the entire playlist. Requires rank >= ADMIN."""
|
||||
self.api.clear_playlist()
|
||||
|
||||
# ── Shows ─────────────────────────────────────────────────────────────────
|
||||
|
||||
def list_shows(self) -> list:
|
||||
"""List scheduled shows. Requires rank >= MOD."""
|
||||
return self.api.list_shows()
|
||||
|
||||
def get_show(self, show_id: int | str) -> dict:
|
||||
"""Get a single show by id. Requires rank >= MOD."""
|
||||
return self.api.get_show(show_id)
|
||||
|
||||
def create_show(self, payload: dict) -> dict:
|
||||
"""Create a show. Requires rank >= MOD."""
|
||||
return self.api.create_show(payload)
|
||||
|
||||
def update_show(self, show_id: int | str, payload: dict) -> dict:
|
||||
"""Update a show by id. Requires rank >= MOD."""
|
||||
return self.api.update_show(show_id, payload)
|
||||
|
||||
def delete_show(self, show_id: int | str):
|
||||
"""Delete a show by id. Requires rank >= ADMIN."""
|
||||
self.api.delete_show(show_id)
|
||||
|
||||
def show_action(self, show_id: int | str, action: str) -> dict:
|
||||
"""
|
||||
Run show action.
|
||||
pause/resume/schedule require rank >= MOD. run/cancel require rank >= ADMIN.
|
||||
"""
|
||||
return self.api.show_action(show_id, action)
|
||||
|
||||
# ── Emotes ────────────────────────────────────────────────────────────────
|
||||
|
||||
def get_emotes(self) -> list:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue