local-coze 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. local_coze/__init__.py +110 -0
  2. local_coze/cli/__init__.py +3 -0
  3. local_coze/cli/chat.py +126 -0
  4. local_coze/cli/cli.py +34 -0
  5. local_coze/cli/constants.py +7 -0
  6. local_coze/cli/db.py +81 -0
  7. local_coze/cli/embedding.py +193 -0
  8. local_coze/cli/image.py +162 -0
  9. local_coze/cli/knowledge.py +195 -0
  10. local_coze/cli/search.py +198 -0
  11. local_coze/cli/utils.py +41 -0
  12. local_coze/cli/video.py +191 -0
  13. local_coze/cli/video_edit.py +888 -0
  14. local_coze/cli/voice.py +351 -0
  15. local_coze/core/__init__.py +25 -0
  16. local_coze/core/client.py +253 -0
  17. local_coze/core/config.py +58 -0
  18. local_coze/core/exceptions.py +67 -0
  19. local_coze/database/__init__.py +29 -0
  20. local_coze/database/client.py +170 -0
  21. local_coze/database/migration.py +342 -0
  22. local_coze/embedding/__init__.py +31 -0
  23. local_coze/embedding/client.py +350 -0
  24. local_coze/embedding/models.py +130 -0
  25. local_coze/image/__init__.py +19 -0
  26. local_coze/image/client.py +110 -0
  27. local_coze/image/models.py +163 -0
  28. local_coze/knowledge/__init__.py +19 -0
  29. local_coze/knowledge/client.py +148 -0
  30. local_coze/knowledge/models.py +45 -0
  31. local_coze/llm/__init__.py +25 -0
  32. local_coze/llm/client.py +317 -0
  33. local_coze/llm/models.py +48 -0
  34. local_coze/memory/__init__.py +14 -0
  35. local_coze/memory/client.py +176 -0
  36. local_coze/s3/__init__.py +12 -0
  37. local_coze/s3/client.py +580 -0
  38. local_coze/s3/models.py +18 -0
  39. local_coze/search/__init__.py +19 -0
  40. local_coze/search/client.py +183 -0
  41. local_coze/search/models.py +57 -0
  42. local_coze/video/__init__.py +17 -0
  43. local_coze/video/client.py +347 -0
  44. local_coze/video/models.py +39 -0
  45. local_coze/video_edit/__init__.py +23 -0
  46. local_coze/video_edit/examples.py +340 -0
  47. local_coze/video_edit/frame_extractor.py +176 -0
  48. local_coze/video_edit/models.py +362 -0
  49. local_coze/video_edit/video_edit.py +631 -0
  50. local_coze/voice/__init__.py +17 -0
  51. local_coze/voice/asr.py +82 -0
  52. local_coze/voice/models.py +86 -0
  53. local_coze/voice/tts.py +94 -0
  54. local_coze-0.0.1.dist-info/METADATA +636 -0
  55. local_coze-0.0.1.dist-info/RECORD +58 -0
  56. local_coze-0.0.1.dist-info/WHEEL +4 -0
  57. local_coze-0.0.1.dist-info/entry_points.txt +3 -0
  58. local_coze-0.0.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,340 @@
1
+ """
2
+ 视频编辑功能示例代码
3
+
4
+ 本文件包含了 video_edit 模块的各种使用示例,包括:
5
+ 1. 视频帧提取(关键帧、固定间隔、固定数量)
6
+ 2. 视频剪辑(裁剪、拼接)
7
+ 3. 字幕处理(添加字幕、音频转字幕)
8
+ 4. 音频处理(提取音频、合成音视频)
9
+ """
10
+
11
+ from local_coze.video_edit import (
12
+ FrameExtractorClient,
13
+ VideoEditClient,
14
+ SubtitleConfig,
15
+ FontPosConfig,
16
+ TextItem,
17
+ OutputSync,
18
+ )
19
+
20
+
21
+ def example_extract_by_key_frame():
22
+ """示例1:按关键帧提取视频帧"""
23
+ print("=" * 50)
24
+ print("示例1:按关键帧提取视频帧")
25
+ print("=" * 50)
26
+
27
+ custom_headers = {"x-run-mode": "test_run"}
28
+ client = FrameExtractorClient(custom_headers=custom_headers)
29
+
30
+ response = client.extract_by_key_frame(
31
+ url="https://example.com/video.mp4"
32
+ )
33
+
34
+ print(f"提取的关键帧数量: {len(response.data)}")
35
+ for i, frame in enumerate(response.data[:3]):
36
+ print(f" 帧 {i + 1}: 时间={frame.time}s, URL={frame.url}")
37
+ print()
38
+
39
+
40
+ def example_extract_by_interval():
41
+ """示例2:按固定时间间隔提取视频帧"""
42
+ print("=" * 50)
43
+ print("示例2:按固定时间间隔提取视频帧")
44
+ print("=" * 50)
45
+
46
+ custom_headers = {"x-run-mode": "test_run"}
47
+ client = FrameExtractorClient(custom_headers=custom_headers)
48
+
49
+ response = client.extract_by_interval(
50
+ url="https://example.com/video.mp4",
51
+ interval=2.0
52
+ )
53
+
54
+ print(f"每 2 秒提取一帧,共提取: {len(response.data)} 帧")
55
+ for i, frame in enumerate(response.data[:3]):
56
+ print(f" 帧 {i + 1}: 时间={frame.time}s, URL={frame.url}")
57
+ print()
58
+
59
+
60
+ def example_extract_by_count():
61
+ """示例3:按固定数量提取视频帧"""
62
+ print("=" * 50)
63
+ print("示例3:按固定数量提取视频帧")
64
+ print("=" * 50)
65
+
66
+ custom_headers = {"x-run-mode": "test_run"}
67
+ client = FrameExtractorClient(custom_headers=custom_headers)
68
+
69
+ response = client.extract_by_count(
70
+ url="https://example.com/video.mp4",
71
+ count=10
72
+ )
73
+
74
+ print(f"均匀提取 10 帧")
75
+ for i, frame in enumerate(response.data):
76
+ print(f" 帧 {i + 1}: 时间={frame.time}s, URL={frame.url}")
77
+ print()
78
+
79
+
80
+ def example_video_trim():
81
+ """示例4:视频裁剪"""
82
+ print("=" * 50)
83
+ print("示例4:视频裁剪")
84
+ print("=" * 50)
85
+
86
+ custom_headers = {"x-run-mode": "test_run"}
87
+ client = VideoEditClient(custom_headers=custom_headers)
88
+
89
+ response = client.video_trim(
90
+ video="https://example.com/video.mp4",
91
+ start_time=10.0,
92
+ end_time=30.0,
93
+ url_expire=3600
94
+ )
95
+
96
+ print(f"裁剪视频: 从 10 秒到 30 秒")
97
+ print(f"结果 URL: {response.url}")
98
+ print()
99
+
100
+
101
+ def example_concat_videos():
102
+ """示例5:视频拼接"""
103
+ print("=" * 50)
104
+ print("示例5:视频拼接")
105
+ print("=" * 50)
106
+
107
+ custom_headers = {"x-run-mode": "test_run"}
108
+ client = VideoEditClient(custom_headers=custom_headers)
109
+
110
+ video_list = [
111
+ "https://example.com/video1.mp4",
112
+ "https://example.com/video2.mp4",
113
+ "https://example.com/video3.mp4"
114
+ ]
115
+
116
+ response = client.concat_videos(
117
+ video_list=video_list,
118
+ url_expire=3600
119
+ )
120
+
121
+ print(f"拼接 {len(video_list)} 个视频")
122
+ print(f"结果 URL: {response.url}")
123
+ print()
124
+
125
+
126
+ def example_add_subtitles_with_text():
127
+ """示例6:添加字幕(使用文本列表)"""
128
+ print("=" * 50)
129
+ print("示例6:添加字幕(使用文本列表)")
130
+ print("=" * 50)
131
+
132
+ custom_headers = {"x-run-mode": "test_run"}
133
+ client = VideoEditClient(custom_headers=custom_headers)
134
+
135
+ subtitle_config = SubtitleConfig(
136
+ font_pos_config=FontPosConfig(
137
+ pos_x="100",
138
+ pos_y="900",
139
+ width="1720",
140
+ height="100"
141
+ ),
142
+ font_size=48,
143
+ font_color="#FFFFFF",
144
+ background_color="#000000",
145
+ background_alpha=0.5
146
+ )
147
+
148
+ text_list = [
149
+ TextItem(start_time=0.0, end_time=3.0, text="你好,世界!"),
150
+ TextItem(start_time=3.0, end_time=6.0, text="欢迎使用视频编辑功能!"),
151
+ TextItem(start_time=6.0, end_time=9.0, text="这是一个字幕示例。"),
152
+ ]
153
+
154
+ response = client.add_subtitles(
155
+ video="https://example.com/video.mp4",
156
+ subtitle_config=subtitle_config,
157
+ text_list=text_list,
158
+ url_expire=3600
159
+ )
160
+
161
+ print(f"添加了 {len(text_list)} 条字幕")
162
+ print(f"结果 URL: {response.url}")
163
+ print()
164
+
165
+
166
+ def example_add_subtitles_with_file():
167
+ """示例7:添加字幕(使用字幕文件)"""
168
+ print("=" * 50)
169
+ print("示例7:添加字幕(使用字幕文件)")
170
+ print("=" * 50)
171
+
172
+ custom_headers = {"x-run-mode": "test_run"}
173
+ client = VideoEditClient(custom_headers=custom_headers)
174
+
175
+ subtitle_config = SubtitleConfig(
176
+ font_pos_config=FontPosConfig(
177
+ pos_x="0",
178
+ pos_y="800",
179
+ width="1920",
180
+ height="200"
181
+ ),
182
+ font_size=36,
183
+ font_color="#FFFF00"
184
+ )
185
+
186
+ response = client.add_subtitles(
187
+ video="https://example.com/video.mp4",
188
+ subtitle_config=subtitle_config,
189
+ subtitle_url="https://example.com/subtitle.srt",
190
+ url_expire=3600
191
+ )
192
+
193
+ print(f"使用字幕文件添加字幕")
194
+ print(f"结果 URL: {response.url}")
195
+ print()
196
+
197
+
198
+ def example_audio_to_subtitle():
199
+ """示例8:音频转字幕"""
200
+ print("=" * 50)
201
+ print("示例8:音频转字幕")
202
+ print("=" * 50)
203
+
204
+ custom_headers = {"x-run-mode": "test_run"}
205
+ client = VideoEditClient(custom_headers=custom_headers)
206
+
207
+ response = client.audio_to_subtitle(
208
+ audio="https://example.com/audio.mp3",
209
+ url_expire=3600
210
+ )
211
+
212
+ print(f"音频转字幕完成")
213
+ print(f"字幕文件 URL: {response.url}")
214
+ print()
215
+
216
+
217
+ def example_audio_extract():
218
+ """示例9:提取视频音频"""
219
+ print("=" * 50)
220
+ print("示例9:提取视频音频")
221
+ print("=" * 50)
222
+
223
+ custom_headers = {"x-run-mode": "test_run"}
224
+ client = VideoEditClient(custom_headers=custom_headers)
225
+
226
+ response = client.extract_audio(
227
+ video="https://example.com/video.mp4",
228
+ url_expire=3600
229
+ )
230
+
231
+ print(f"提取音频完成")
232
+ print(f"音频文件 URL: {response.url}")
233
+ print()
234
+
235
+
236
+ def example_compile_video_audio():
237
+ """示例10:合成视频和音频"""
238
+ print("=" * 50)
239
+ print("示例10:合成视频和音频")
240
+ print("=" * 50)
241
+
242
+ custom_headers = {"x-run-mode": "test_run"}
243
+ client = VideoEditClient(custom_headers=custom_headers)
244
+
245
+ response = client.compile_video_audio(
246
+ video="https://example.com/video_no_audio.mp4",
247
+ audio="https://example.com/audio.mp3",
248
+ url_expire=3600
249
+ )
250
+
251
+ print(f"合成视频和音频完成")
252
+ print(f"结果 URL: {response.url}")
253
+ print()
254
+
255
+
256
+ def example_async_operations():
257
+ """示例11:异步操作示例"""
258
+ print("=" * 50)
259
+ print("示例11:异步操作示例")
260
+ print("=" * 50)
261
+
262
+ import asyncio
263
+
264
+ async def async_example():
265
+ custom_headers = {"x-run-mode": "test_run"}
266
+ frame_client = FrameExtractorClient(custom_headers=custom_headers)
267
+ edit_client = VideoEditClient(custom_headers=custom_headers)
268
+
269
+ frame_response = await frame_client.extract_by_key_frame_async(
270
+ url="https://example.com/video.mp4"
271
+ )
272
+ print(f"异步提取关键帧: {len(frame_response.data)} 帧")
273
+
274
+ trim_response = await edit_client.video_trim_async(
275
+ video="https://example.com/video.mp4",
276
+ start_time=5.0,
277
+ end_time=15.0
278
+ )
279
+ print(f"异步裁剪视频: {trim_response.url}")
280
+
281
+ asyncio.run(async_example())
282
+ print()
283
+
284
+
285
+ def example_with_output_sync():
286
+ """示例12:使用同步输出配置"""
287
+ print("=" * 50)
288
+ print("示例12:使用同步输出配置")
289
+ print("=" * 50)
290
+
291
+ custom_headers = {"x-run-mode": "test_run"}
292
+ client = VideoEditClient(custom_headers=custom_headers)
293
+
294
+ output_sync = OutputSync(
295
+ bucket="my-bucket",
296
+ key="output/trimmed_video.mp4"
297
+ )
298
+
299
+ response = client.video_trim(
300
+ video="https://example.com/video.mp4",
301
+ start_time=0.0,
302
+ end_time=10.0,
303
+ output_sync=output_sync
304
+ )
305
+
306
+ print(f"视频已同步到指定位置")
307
+ print(f"结果 URL: {response.url}")
308
+ print()
309
+
310
+
311
+ def main():
312
+ """运行所有示例"""
313
+ print("\n" + "=" * 50)
314
+ print("视频编辑功能示例集合")
315
+ print("=" * 50 + "\n")
316
+
317
+ try:
318
+ example_extract_by_key_frame()
319
+ example_extract_by_interval()
320
+ example_extract_by_count()
321
+ example_video_trim()
322
+ example_concat_videos()
323
+ example_add_subtitles_with_text()
324
+ example_add_subtitles_with_file()
325
+ example_audio_to_subtitle()
326
+ example_audio_extract()
327
+ example_compile_video_audio()
328
+ example_async_operations()
329
+ example_with_output_sync()
330
+
331
+ print("=" * 50)
332
+ print("所有示例运行完成!")
333
+ print("=" * 50)
334
+
335
+ except Exception as e:
336
+ print(f"\n运行示例时出错: {e}")
337
+
338
+
339
+ if __name__ == "__main__":
340
+ main()
@@ -0,0 +1,176 @@
1
+ import asyncio
2
+ from typing import Dict, Optional
3
+
4
+ from cozeloop.decorator import observe
5
+ from coze_coding_utils.runtime_ctx.context import Context
6
+
7
+ from ..core.client import BaseClient
8
+ from ..core.config import Config
9
+ from ..core.exceptions import APIError
10
+ from .models import (
11
+ FrameExtractorByIntervalRequest,
12
+ FrameExtractorByKeyFrameRequest,
13
+ FrameExtractorResponse,
14
+ )
15
+
16
+
17
+ class FrameExtractorClient(BaseClient):
18
+ def __init__(
19
+ self,
20
+ config: Optional[Config] = None,
21
+ ctx: Optional[Context] = None,
22
+ custom_headers: Optional[Dict[str, str]] = None,
23
+ verbose: bool = False,
24
+ ):
25
+ super().__init__(config, ctx, custom_headers, verbose)
26
+ self.base_url = self.config.base_url
27
+
28
+ @observe
29
+ def extract_by_key_frame(self, url: str) -> FrameExtractorResponse:
30
+ """
31
+ 按关键帧抽取视频帧
32
+
33
+ Args:
34
+ url: 视频 URL 链接
35
+
36
+ Returns:
37
+ FrameExtractorResponse: 抽帧结果响应
38
+
39
+ Raises:
40
+ APIError: 当抽帧失败时
41
+ """
42
+ request = FrameExtractorByKeyFrameRequest(url=url)
43
+
44
+ response = self._request(
45
+ method="POST",
46
+ url=f"{self.base_url}/api/v1/integration/video_editing_utils?tool_name=frame_extractor_by_key_frame",
47
+ json=request.to_api_request(),
48
+ )
49
+
50
+ if response.get("code") != 0:
51
+ raise APIError(
52
+ f"关键帧抽取失败: {response.get('message', 'Unknown error')}",
53
+ code=response.get("code"),
54
+ )
55
+
56
+ return FrameExtractorResponse(**response)
57
+
58
+ @observe
59
+ def extract_by_interval(self, url: str, interval_ms: int) -> FrameExtractorResponse:
60
+ """
61
+ 等时抽帧,从视频开始,每隔 interval_ms 时间抽帧一次。
62
+
63
+ Args:
64
+ url: 视频 URL 链接
65
+ interval_ms: 间隔抽帧时间,单位: ms
66
+
67
+ Returns:
68
+ FrameExtractorResponse: 抽帧结果响应
69
+
70
+ Raises:
71
+ APIError: 当抽帧失败时
72
+ """
73
+ request = FrameExtractorByIntervalRequest(url=url, interval_ms=interval_ms)
74
+
75
+ response = self._request(
76
+ method="POST",
77
+ url=f"{self.base_url}/api/v1/integration/video_editing_utils?tool_name=frame_extractor_by_interval",
78
+ json=request.to_api_request(),
79
+ )
80
+
81
+ if response.get("code") != 0:
82
+ raise APIError(
83
+ f"间隔抽帧失败: {response.get('message', 'Unknown error')}",
84
+ code=response.get("code"),
85
+ )
86
+
87
+ return FrameExtractorResponse(**response)
88
+
89
+ @observe
90
+ def extract_by_count(self, url: str, count: int) -> FrameExtractorResponse:
91
+ """
92
+ 定数抽帧,根据视频时长/抽取帧数计算间隔动态抽帧。
93
+
94
+ Args:
95
+ url: 视频 URL 链接
96
+ count: 抽取多少个帧
97
+
98
+ Returns:
99
+ FrameExtractorResponse: 抽帧结果响应
100
+
101
+ Raises:
102
+ APIError: 当抽帧失败时
103
+ """
104
+ from .models import FrameExtractorByInterval
105
+
106
+ request = FrameExtractorByInterval(url=url, count=count)
107
+
108
+ response = self._request(
109
+ method="POST",
110
+ url=f"{self.base_url}/api/v1/integration/video_editing_utils?tool_name=frame_extractor_by_count",
111
+ json=request.to_api_request(),
112
+ )
113
+
114
+ if response.get("code") != 0:
115
+ raise APIError(
116
+ f"按数量抽帧失败: {response.get('message', 'Unknown error')}",
117
+ code=response.get("code"),
118
+ )
119
+
120
+ return FrameExtractorResponse(**response)
121
+
122
+ async def extract_by_key_frame_async(self, url: str) -> FrameExtractorResponse:
123
+ """
124
+ 按关键帧抽取视频帧(异步版本)
125
+
126
+ Args:
127
+ url: 视频 URL 链接
128
+
129
+ Returns:
130
+ FrameExtractorResponse: 抽帧结果响应
131
+
132
+ Raises:
133
+ APIError: 当抽帧失败时
134
+ """
135
+ loop = asyncio.get_event_loop()
136
+ return await loop.run_in_executor(None, self.extract_by_key_frame, url)
137
+
138
+ async def extract_by_interval_async(
139
+ self, url: str, interval_ms: int
140
+ ) -> FrameExtractorResponse:
141
+ """
142
+ 等时抽帧,从视频开始,每隔 interval_ms 时间抽帧一次(异步版本)
143
+
144
+ Args:
145
+ url: 视频 URL 链接
146
+ interval_ms: 间隔抽帧时间,单位: ms
147
+
148
+ Returns:
149
+ FrameExtractorResponse: 抽帧结果响应
150
+
151
+ Raises:
152
+ APIError: 当抽帧失败时
153
+ """
154
+ loop = asyncio.get_event_loop()
155
+ return await loop.run_in_executor(
156
+ None, self.extract_by_interval, url, interval_ms
157
+ )
158
+
159
+ async def extract_by_count_async(
160
+ self, url: str, count: int
161
+ ) -> FrameExtractorResponse:
162
+ """
163
+ 定数抽帧,根据视频时长/抽取帧数计算间隔动态抽帧(异步版本)
164
+
165
+ Args:
166
+ url: 视频 URL 链接
167
+ count: 抽取多少个帧
168
+
169
+ Returns:
170
+ FrameExtractorResponse: 抽帧结果响应
171
+
172
+ Raises:
173
+ APIError: 当抽帧失败时
174
+ """
175
+ loop = asyncio.get_event_loop()
176
+ return await loop.run_in_executor(None, self.extract_by_count, url, count)