Skip to content

Commit 577c358

Browse files
committed
initial create_clip_from_vod
1 parent 45af4cf commit 577c358

File tree

3 files changed

+107
-1
lines changed

3 files changed

+107
-1
lines changed

twitchio/http.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
ClipsResponseData,
106106
ContentClassificationLabelsResponse,
107107
CreateChannelStreamScheduleSegmentResponse,
108+
CreateClipFromVodResponse,
108109
CreateClipResponse,
109110
CreateStreamMarkerResponse,
110111
CreatorGoalsResponse,
@@ -1350,6 +1351,33 @@ async def post_create_clip(
13501351
route: Route = Route("POST", "clips", params=params, token_for=token_for)
13511352
return await self.request_json(route)
13521353

1354+
@handle_user_ids()
1355+
async def post_create_clip_from_vod(
1356+
self,
1357+
*,
1358+
vod_id: str,
1359+
vod_offset: int,
1360+
title: str,
1361+
broadcaster_id: str | int,
1362+
editor_id: str | PartialUser,
1363+
token_for: str | PartialUser | None = None,
1364+
duration: float | None = None,
1365+
) -> CreateClipFromVodResponse:
1366+
params: dict[str, str | float] = {
1367+
"broadcaster_id": broadcaster_id,
1368+
"editor_id": str(editor_id),
1369+
"vod_id": vod_id,
1370+
"vod_offset": vod_offset,
1371+
"title": title,
1372+
}
1373+
1374+
if duration is not None:
1375+
params["duration"] = duration
1376+
1377+
route: Route = Route("POST", "videos/clips", params=params, token_for=token_for)
1378+
1379+
return await self.request_json(route)
1380+
13531381
### Conduits ###
13541382

13551383
async def delete_conduit(self, conduit_id: str, /) -> None:

twitchio/types_/responses.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
"ConduitPayload",
5353
"ContentClassificationLabelData",
5454
"ContentClassificationLabelsResponse",
55+
"CreateClipFromVodResponse",
56+
"CreateClipResponse",
5557
"EventsubSubscriptionResponse",
5658
"EventsubSubscriptionResponseData",
5759
"ExtensionAnalyticsResponse",
@@ -660,10 +662,19 @@ class CreateClipResponseData(TypedDict):
660662
id: str
661663

662664

665+
class CreateClipFromVodResponseData(TypedDict):
666+
edit_url: str
667+
id: str
668+
669+
663670
class CreateClipResponse(TypedDict):
664671
data: list[CreateClipResponseData]
665672

666673

674+
class CreateClipFromVodResponse(TypedDict):
675+
data: list[CreateClipFromVodResponseData]
676+
677+
667678
class ClipsResponseData(TypedDict):
668679
id: str
669680
url: str

twitchio/user.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1263,7 +1263,6 @@ async def create_clip(
12631263
.. note::
12641264
Requires a user access token that includes the ``clips:edit`` scope.
12651265
1266-
12671266
.. warning::
12681267
The `has_delay` argument has been removed by Twitch and no longer has any effect.
12691268
It has been retained to avoid breaking changes for users who still have it set.
@@ -1297,6 +1296,74 @@ async def create_clip(
12971296
data = await self._http.post_create_clip(broadcaster_id=self.id, token_for=token_for, title=title, duration=duration)
12981297
return CreatedClip(data["data"][0])
12991298

1299+
async def create_clip_from_vod(
1300+
self,
1301+
*,
1302+
editor_id: str | PartialUser,
1303+
vod_id: str,
1304+
vod_offset: int,
1305+
title: str,
1306+
duration: float | None = None,
1307+
token_for: str | PartialUser | None = None,
1308+
) -> CreatedClip:
1309+
"""|coro|
1310+
1311+
Creates a clip from a broadcaster's VOD on behalf of the broadcaster or an editor of the channel.
1312+
Since a live stream is actively creating a VOD, this endpoint can also be used to create a clip from earlier in the current stream.
1313+
1314+
The duration of a clip can be from 5 seconds to 60 seconds in length, with a default of 30 seconds if not specified.
1315+
1316+
`vod_offset` indicates where the clip will end. In other words, the clip will start at (`vod_offset` - `duration`) and end at `vod_offset`.
1317+
This means that the value of `vod_offset` must greater than or equal to the value of duration.
1318+
1319+
The URL in the response's `edit_url` field allows you to edit the clip's title, feature the clip, create a portrait version of the clip, download the clip media, and share the clip directly to social platforms.
1320+
1321+
.. note::
1322+
Requires an app access token or user access token that includes the ``editor:manage:clips`` or ``channel:manage:clips`` scope.
1323+
1324+
Parameters
1325+
----------
1326+
editor_id: str | PartialUser,
1327+
The user ID, or PartialUser, of the editor for the channel you want to create a clip for.
1328+
This can be the broadcaster.
1329+
vod_id: str,
1330+
ID of the VOD the user wants to clip.
1331+
vod_offset: int,
1332+
Offset in the VOD to create the clip.
1333+
The clip will start at (`vod_offset` - `duration`) and end at `vod_offset`.
1334+
This means that the value of `vod_offset` must greater than or equal to the value of duration.
1335+
title: str,
1336+
The title of the clip.
1337+
duration: float | None = None,
1338+
The length of the clip, in seconds. Precision is 0.1. Defaults to 30. Min: 5 seconds, Max: 60 seconds.
1339+
token_for: str | PartialUser | None
1340+
An optional user token to use instead of the default app token.
1341+
1342+
Returns
1343+
-------
1344+
CreatedClip
1345+
The CreatedClip object.
1346+
Raises
1347+
------
1348+
ValueError
1349+
Clip duration must be between 5 and 60, with precision of 0.1
1350+
"""
1351+
if duration is not None and not (5 <= duration <= 60):
1352+
raise ValueError("Clip duration must be between 5 and 60, with precision of 0.1")
1353+
1354+
from .models.clips import CreatedClip
1355+
1356+
data = await self._http.post_create_clip(
1357+
broadcaster_id=self.id,
1358+
title=title,
1359+
duration=duration,
1360+
vod_id=vod_id,
1361+
vod_offset=vod_offset,
1362+
editor_id=editor_id,
1363+
token_for=token_for,
1364+
)
1365+
return CreatedClip(data["data"][0])
1366+
13001367
def fetch_clips(
13011368
self,
13021369
*,

0 commit comments

Comments
 (0)