VibesAI-api 1.3.2__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.
- server/__init__.py +3 -0
- server/app.py +332 -0
- vibes_api/__init__.py +121 -0
- vibes_api/cli.py +852 -0
- vibes_api/client.py +3972 -0
- vibes_api/composition.py +852 -0
- vibes_api/ingredients.py +206 -0
- vibes_api/models.py +165 -0
- vibesai_api-1.3.2.dist-info/METADATA +600 -0
- vibesai_api-1.3.2.dist-info/RECORD +14 -0
- vibesai_api-1.3.2.dist-info/WHEEL +5 -0
- vibesai_api-1.3.2.dist-info/entry_points.txt +2 -0
- vibesai_api-1.3.2.dist-info/licenses/LICENSE +28 -0
- vibesai_api-1.3.2.dist-info/top_level.txt +2 -0
server/__init__.py
ADDED
server/app.py
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
"""FastAPI server exposing VibesClient as a REST API."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import os, sys, threading
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
from fastapi import FastAPI, HTTPException, Header, Depends
|
|
6
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
10
|
+
from vibes_api import VibesClient, VibesAPIError
|
|
11
|
+
|
|
12
|
+
_client: Optional[VibesClient] = None
|
|
13
|
+
_lock = threading.Lock()
|
|
14
|
+
|
|
15
|
+
def get_client() -> VibesClient:
|
|
16
|
+
global _client
|
|
17
|
+
if _client is None:
|
|
18
|
+
with _lock:
|
|
19
|
+
if _client is None:
|
|
20
|
+
ms = os.environ.get("VIBES_META_SESSION")
|
|
21
|
+
if not ms:
|
|
22
|
+
raise HTTPException(500, "VIBES_META_SESSION env var not set.")
|
|
23
|
+
cf = os.environ.get("VIBES_COOKIE_FILE")
|
|
24
|
+
if cf and os.path.exists(cf):
|
|
25
|
+
try:
|
|
26
|
+
with open(cf) as f: ms = f.read().strip()
|
|
27
|
+
except: pass
|
|
28
|
+
def save(c):
|
|
29
|
+
if cf:
|
|
30
|
+
try:
|
|
31
|
+
with open(cf, "w") as f: f.write(c)
|
|
32
|
+
except: pass
|
|
33
|
+
_client = VibesClient(meta_session=ms, auto_refresh=True,
|
|
34
|
+
background_refresh=True,
|
|
35
|
+
refresh_interval=float(os.environ.get("VIBES_REFRESH_INTERVAL","1500")),
|
|
36
|
+
on_cookie_refresh=save)
|
|
37
|
+
return _client
|
|
38
|
+
|
|
39
|
+
def verify_api_key(x_api_key: Optional[str] = Header(None, alias="X-API-Key"),
|
|
40
|
+
authorization: Optional[str] = Header(None)):
|
|
41
|
+
exp = os.environ.get("VIBES_API_KEY")
|
|
42
|
+
if not exp: return
|
|
43
|
+
prov = x_api_key or (authorization[7:] if authorization and authorization.startswith("Bearer ") else None)
|
|
44
|
+
if prov != exp:
|
|
45
|
+
raise HTTPException(401, "Invalid or missing API key.")
|
|
46
|
+
|
|
47
|
+
def _err(e):
|
|
48
|
+
if isinstance(e, VibesAPIError):
|
|
49
|
+
raise HTTPException(e.status or 500, detail={"error": str(e), "code": e.code, "response": e.response})
|
|
50
|
+
raise HTTPException(500, str(e))
|
|
51
|
+
|
|
52
|
+
class PC(BaseModel):
|
|
53
|
+
name: str = "Untitled"
|
|
54
|
+
composition: Optional[dict] = None
|
|
55
|
+
class PU(BaseModel):
|
|
56
|
+
name: Optional[str] = None
|
|
57
|
+
composition: Optional[dict] = None
|
|
58
|
+
class VG(BaseModel):
|
|
59
|
+
project_id: str; prompt: str; aspect_ratio: str = "9:16"; resolution: str = "480p"
|
|
60
|
+
variations: int = 4; video_model: str = "midjen-short"; image_model: str = "midjen-base"
|
|
61
|
+
prompt_model: str = "gemini-2.5-flash"; ingredients: Optional[List[dict]] = None
|
|
62
|
+
create_ingredients: Optional[List[dict]] = None; start_frame: Optional[dict] = None
|
|
63
|
+
end_frame: Optional[dict] = None; moodboard: Optional[dict] = None
|
|
64
|
+
poll: bool = True; poll_timeout: float = 300.0
|
|
65
|
+
class IG(BaseModel):
|
|
66
|
+
project_id: str; prompt: str; aspect_ratio: str = "1:1"; resolution: str = "480p"
|
|
67
|
+
variations: int = 1; image_model: str = "midjen-base"; prompt_model: str = "gemini-2.5-flash"
|
|
68
|
+
ingredients: Optional[List[dict]] = None; create_ingredients: Optional[List[dict]] = None
|
|
69
|
+
moodboard: Optional[dict] = None
|
|
70
|
+
class IE(BaseModel):
|
|
71
|
+
source_image_ent_id: str; edit_prompt: str; project_id: Optional[str] = None
|
|
72
|
+
class PE(BaseModel):
|
|
73
|
+
prompt: str; project_id: Optional[str] = None; batch_type: str = "videos"
|
|
74
|
+
class TT(BaseModel):
|
|
75
|
+
text: str; voice: str; output_format: str = "mp3"; language: Optional[str] = None
|
|
76
|
+
class LR(BaseModel):
|
|
77
|
+
project_id: str; image_prompt: str; script: str; audio_url: str
|
|
78
|
+
audio_duration_ms: int; engine: str = "midjen"; ingredients: Optional[List[dict]] = None
|
|
79
|
+
aspect_ratio: Optional[str] = None; music_track: Optional[dict] = None
|
|
80
|
+
custom_motion_prompt: Optional[str] = None
|
|
81
|
+
class EV(BaseModel):
|
|
82
|
+
project_id: str; batch_id: str; content_id: Optional[str] = None
|
|
83
|
+
prompt: Optional[str] = None; poll: bool = True; poll_timeout: float = 300.0
|
|
84
|
+
class EDV(BaseModel):
|
|
85
|
+
project_id: str; batch_id: str; content_id: Optional[str] = None
|
|
86
|
+
prompt: str; poll: bool = True; poll_timeout: float = 300.0
|
|
87
|
+
class SL(BaseModel):
|
|
88
|
+
entity_type: str; entity_id: str; expires_at: Optional[str] = None; max_uses: Optional[int] = None
|
|
89
|
+
class IC(BaseModel):
|
|
90
|
+
name: str; ingredient_type: str; source_image_ent_id: Optional[str] = None
|
|
91
|
+
image_url: Optional[str] = None; description: Optional[str] = None
|
|
92
|
+
class TC(BaseModel):
|
|
93
|
+
input: str; instructions: Optional[str] = None; tools: Optional[List[dict]] = None; composition: Optional[dict] = None
|
|
94
|
+
class TE(BaseModel):
|
|
95
|
+
composition: dict
|
|
96
|
+
class PR(BaseModel):
|
|
97
|
+
content_item_id: str; batch_id: Optional[str] = None; caption: Optional[str] = None
|
|
98
|
+
audio_types: Optional[List[str]] = None; prompt: Optional[str] = None
|
|
99
|
+
image_prompt: Optional[str] = None; video_prompt: Optional[str] = None
|
|
100
|
+
class PM(BaseModel):
|
|
101
|
+
prompt: str
|
|
102
|
+
class UI(BaseModel):
|
|
103
|
+
image_base64: str
|
|
104
|
+
|
|
105
|
+
app = FastAPI(title="VibesAI API", version="1.3.0", docs_url="/docs", redoc_url="/redoc")
|
|
106
|
+
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
|
|
107
|
+
AUTH = [Depends(verify_api_key)]
|
|
108
|
+
|
|
109
|
+
@app.get("/", tags=["Health"])
|
|
110
|
+
async def root(): return {"name": "VibesAI API", "version": "1.3.0", "docs": "/docs"}
|
|
111
|
+
|
|
112
|
+
@app.get("/health", tags=["Health"])
|
|
113
|
+
async def health():
|
|
114
|
+
try:
|
|
115
|
+
u = get_client().get_me(); return {"status": "healthy", "user": u.get("username")}
|
|
116
|
+
except Exception as e: return {"status": "unhealthy", "error": str(e)}
|
|
117
|
+
|
|
118
|
+
@app.get("/api/v1/me", tags=["Auth"], dependencies=AUTH)
|
|
119
|
+
async def me():
|
|
120
|
+
try: return get_client().get_me()
|
|
121
|
+
except Exception as e: _err(e)
|
|
122
|
+
|
|
123
|
+
@app.get("/api/v1/check-token", tags=["Auth"], dependencies=AUTH)
|
|
124
|
+
async def check_token():
|
|
125
|
+
try: return {"valid": get_client().check_token()}
|
|
126
|
+
except Exception as e: _err(e)
|
|
127
|
+
|
|
128
|
+
@app.get("/api/v1/current-cookie", tags=["Auth"], dependencies=AUTH)
|
|
129
|
+
async def current_cookie(): return {"meta_session": get_client().get_current_cookie()}
|
|
130
|
+
|
|
131
|
+
@app.get("/api/v1/projects", tags=["Projects"], dependencies=AUTH)
|
|
132
|
+
async def list_projects(limit: int = 25, offset: int = 0, sort: str = "newest", search: Optional[str] = None):
|
|
133
|
+
try: return get_client().list_projects(limit=limit, offset=offset, sort=sort, search=search)
|
|
134
|
+
except Exception as e: _err(e)
|
|
135
|
+
|
|
136
|
+
@app.post("/api/v1/projects", tags=["Projects"], dependencies=AUTH)
|
|
137
|
+
async def create_project(b: PC):
|
|
138
|
+
try: return get_client().create_project(name=b.name, composition=b.composition)
|
|
139
|
+
except Exception as e: _err(e)
|
|
140
|
+
|
|
141
|
+
@app.get("/api/v1/projects/{pid}", tags=["Projects"], dependencies=AUTH)
|
|
142
|
+
async def get_project(pid: str):
|
|
143
|
+
try: return get_client().get_project(pid)
|
|
144
|
+
except Exception as e: _err(e)
|
|
145
|
+
|
|
146
|
+
@app.put("/api/v1/projects/{pid}", tags=["Projects"], dependencies=AUTH)
|
|
147
|
+
async def update_project(pid: str, b: PU):
|
|
148
|
+
try: return get_client().update_project(pid, name=b.name, composition=b.composition)
|
|
149
|
+
except Exception as e: _err(e)
|
|
150
|
+
|
|
151
|
+
@app.delete("/api/v1/projects/{pid}", tags=["Projects"], dependencies=AUTH)
|
|
152
|
+
async def delete_project(pid: str, delete_assets: bool = False):
|
|
153
|
+
try: get_client().delete_project(pid, delete_assets=delete_assets); return {"success": True}
|
|
154
|
+
except Exception as e: _err(e)
|
|
155
|
+
|
|
156
|
+
@app.post("/api/v1/videos/generate", tags=["Videos"], dependencies=AUTH)
|
|
157
|
+
async def gen_video(b: VG):
|
|
158
|
+
try: return get_client().generate_video(project_id=b.project_id, prompt=b.prompt,
|
|
159
|
+
aspect_ratio=b.aspect_ratio, resolution=b.resolution, variations=b.variations,
|
|
160
|
+
video_model=b.video_model, image_model=b.image_model, prompt_model=b.prompt_model,
|
|
161
|
+
ingredients=b.ingredients, create_ingredients=b.create_ingredients,
|
|
162
|
+
start_frame=b.start_frame, end_frame=b.end_frame, moodboard=b.moodboard,
|
|
163
|
+
poll=b.poll, poll_timeout=b.poll_timeout)
|
|
164
|
+
except Exception as e: _err(e)
|
|
165
|
+
|
|
166
|
+
@app.post("/api/v1/videos/extend", tags=["Videos"], dependencies=AUTH)
|
|
167
|
+
async def ext_video(b: EV):
|
|
168
|
+
try:
|
|
169
|
+
c = get_client(); batch = c.get_batch(b.batch_id)
|
|
170
|
+
src = next((x for x in batch.get("content",[]) if x["id"]==b.content_id), None) or (batch["content"][0] if batch.get("content") else None)
|
|
171
|
+
if not src: raise HTTPException(404, "No content item found")
|
|
172
|
+
return c.extend_video(project_id=b.project_id, source_video=src, prompt=b.prompt, poll=b.poll, poll_timeout=b.poll_timeout)
|
|
173
|
+
except HTTPException: raise
|
|
174
|
+
except Exception as e: _err(e)
|
|
175
|
+
|
|
176
|
+
@app.post("/api/v1/videos/edit", tags=["Videos"], dependencies=AUTH)
|
|
177
|
+
async def edit_video(b: EDV):
|
|
178
|
+
try:
|
|
179
|
+
c = get_client(); batch = c.get_batch(b.batch_id)
|
|
180
|
+
src = next((x for x in batch.get("content",[]) if x["id"]==b.content_id), None) or (batch["content"][0] if batch.get("content") else None)
|
|
181
|
+
if not src: raise HTTPException(404, "No content item found")
|
|
182
|
+
return c.edit_video(project_id=b.project_id, source_video=src, prompt=b.prompt, poll=b.poll, poll_timeout=b.poll_timeout)
|
|
183
|
+
except HTTPException: raise
|
|
184
|
+
except Exception as e: _err(e)
|
|
185
|
+
|
|
186
|
+
@app.post("/api/v1/images/generate", tags=["Images"], dependencies=AUTH)
|
|
187
|
+
async def gen_image(b: IG):
|
|
188
|
+
try: return get_client().generate_image(project_id=b.project_id, prompt=b.prompt,
|
|
189
|
+
aspect_ratio=b.aspect_ratio, resolution=b.resolution, variations=b.variations,
|
|
190
|
+
image_model=b.image_model, prompt_model=b.prompt_model, ingredients=b.ingredients,
|
|
191
|
+
create_ingredients=b.create_ingredients, moodboard=b.moodboard)
|
|
192
|
+
except Exception as e: _err(e)
|
|
193
|
+
|
|
194
|
+
@app.post("/api/v1/images/edit", tags=["Images"], dependencies=AUTH)
|
|
195
|
+
async def edit_image(b: IE):
|
|
196
|
+
try: return get_client().edit_image(source_image_ent_id=b.source_image_ent_id, edit_prompt=b.edit_prompt, project_id=b.project_id)
|
|
197
|
+
except Exception as e: _err(e)
|
|
198
|
+
|
|
199
|
+
@app.post("/api/v1/upload/image", tags=["Uploads"], dependencies=AUTH)
|
|
200
|
+
async def upload_image(b: UI):
|
|
201
|
+
try: return get_client().upload_image(b.image_base64)
|
|
202
|
+
except Exception as e: _err(e)
|
|
203
|
+
|
|
204
|
+
@app.post("/api/v1/prompts/enhance", tags=["Prompts"], dependencies=AUTH)
|
|
205
|
+
async def enhance_prompt(b: PE):
|
|
206
|
+
try: return {"variations": get_client().enhance_prompt(prompt=b.prompt, project_id=b.project_id, batch_type=b.batch_type)}
|
|
207
|
+
except Exception as e: _err(e)
|
|
208
|
+
|
|
209
|
+
@app.get("/api/v1/voices", tags=["TTS"], dependencies=AUTH)
|
|
210
|
+
async def list_voices():
|
|
211
|
+
try: return {"voices": get_client().list_voices()}
|
|
212
|
+
except Exception as e: _err(e)
|
|
213
|
+
|
|
214
|
+
@app.post("/api/v1/tts", tags=["TTS"], dependencies=AUTH)
|
|
215
|
+
async def tts(b: TT):
|
|
216
|
+
try: return get_client().tts(text=b.text, voice=b.voice, output_format=b.output_format, language=b.language)
|
|
217
|
+
except Exception as e: _err(e)
|
|
218
|
+
|
|
219
|
+
@app.get("/api/v1/media", tags=["Media"], dependencies=AUTH)
|
|
220
|
+
async def list_media(limit: int = 50, offset: int = 0, type: Optional[str] = None, search: Optional[str] = None):
|
|
221
|
+
try: return get_client().list_media(limit=limit, offset=offset, type=type, search=search)
|
|
222
|
+
except Exception as e: _err(e)
|
|
223
|
+
|
|
224
|
+
@app.get("/api/v1/media/{item_id}/download", tags=["Media"], dependencies=AUTH)
|
|
225
|
+
async def download_media(item_id: str, type: str = "video"):
|
|
226
|
+
from fastapi import Response; import tempfile
|
|
227
|
+
try:
|
|
228
|
+
c = get_client(); suffix = ".mp4" if type=="video" else ".png"
|
|
229
|
+
with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as f: path = f.name
|
|
230
|
+
if type=="video": c.download_video(item_id, path)
|
|
231
|
+
else: c.download_image(item_id, path)
|
|
232
|
+
with open(path,"rb") as f: content = f.read()
|
|
233
|
+
os.unlink(path)
|
|
234
|
+
return Response(content=content, media_type="video/mp4" if type=="video" else "image/png")
|
|
235
|
+
except Exception as e: _err(e)
|
|
236
|
+
|
|
237
|
+
@app.delete("/api/v1/media/{item_id}", tags=["Media"], dependencies=AUTH)
|
|
238
|
+
async def del_media(item_id: str):
|
|
239
|
+
try: get_client().delete_content_item(item_id); return {"success": True}
|
|
240
|
+
except Exception as e: _err(e)
|
|
241
|
+
|
|
242
|
+
@app.get("/api/v1/batches", tags=["Batches"], dependencies=AUTH)
|
|
243
|
+
async def list_batches(limit: int = 12, offset: int = 0, project_id: Optional[str] = None):
|
|
244
|
+
try: return get_client().list_batches(limit=limit, offset=offset, project_id=project_id)
|
|
245
|
+
except Exception as e: _err(e)
|
|
246
|
+
|
|
247
|
+
@app.get("/api/v1/batches/{bid}", tags=["Batches"], dependencies=AUTH)
|
|
248
|
+
async def get_batch(bid: str):
|
|
249
|
+
try: return get_client().get_batch(bid)
|
|
250
|
+
except Exception as e: _err(e)
|
|
251
|
+
|
|
252
|
+
@app.post("/api/v1/batches/{bid}/poll", tags=["Batches"], dependencies=AUTH)
|
|
253
|
+
async def poll_batch(bid: str, timeout: float = 180.0):
|
|
254
|
+
try: return get_client().poll_batch(bid, timeout=timeout)
|
|
255
|
+
except Exception as e: _err(e)
|
|
256
|
+
|
|
257
|
+
@app.get("/api/v1/ingredients", tags=["Ingredients"], dependencies=AUTH)
|
|
258
|
+
async def list_ingredients(owner_filter: str = "LIBRARY", ingredient_type: Optional[str] = None):
|
|
259
|
+
try: return {"ingredients": get_client().list_ingredients(owner_filter=owner_filter, ingredient_type=ingredient_type)}
|
|
260
|
+
except Exception as e: _err(e)
|
|
261
|
+
|
|
262
|
+
@app.post("/api/v1/ingredients", tags=["Ingredients"], dependencies=AUTH)
|
|
263
|
+
async def create_ingredient(b: IC):
|
|
264
|
+
try: return get_client().create_ingredient(name=b.name, ingredient_type=b.ingredient_type,
|
|
265
|
+
source_image_ent_id=b.source_image_ent_id, image_url=b.image_url, description=b.description)
|
|
266
|
+
except Exception as e: _err(e)
|
|
267
|
+
|
|
268
|
+
@app.delete("/api/v1/ingredients/{iid}", tags=["Ingredients"], dependencies=AUTH)
|
|
269
|
+
async def del_ingredient(iid: str):
|
|
270
|
+
try: get_client().delete_ingredient(iid); return {"success": True}
|
|
271
|
+
except Exception as e: _err(e)
|
|
272
|
+
|
|
273
|
+
@app.post("/api/v1/share-links", tags=["Share"], dependencies=AUTH)
|
|
274
|
+
async def create_share(b: SL):
|
|
275
|
+
try: return get_client().create_share_link(b.entity_type, b.entity_id, expires_at=b.expires_at, max_uses=b.max_uses)
|
|
276
|
+
except Exception as e: _err(e)
|
|
277
|
+
|
|
278
|
+
@app.get("/api/v1/share-links", tags=["Share"], dependencies=AUTH)
|
|
279
|
+
async def list_shares(entity_type: str, entity_id: str):
|
|
280
|
+
try: return {"shareLinks": get_client().list_share_links(entity_type, entity_id)}
|
|
281
|
+
except Exception as e: _err(e)
|
|
282
|
+
|
|
283
|
+
@app.post("/api/v1/timeline/chat", tags=["Timeline"], dependencies=AUTH)
|
|
284
|
+
async def timeline_chat(b: TC):
|
|
285
|
+
try:
|
|
286
|
+
events = []
|
|
287
|
+
for ev in get_client().timeline_chat(b.input, instructions=b.instructions, tools=b.tools, composition=b.composition):
|
|
288
|
+
events.append(ev)
|
|
289
|
+
if ev.get("type") in ("completed","error"): break
|
|
290
|
+
return {"events": events}
|
|
291
|
+
except Exception as e: _err(e)
|
|
292
|
+
|
|
293
|
+
@app.post("/api/v1/timeline/export", tags=["Timeline"], dependencies=AUTH)
|
|
294
|
+
async def export_timeline(project_id: str, b: TE):
|
|
295
|
+
from fastapi import Response
|
|
296
|
+
try:
|
|
297
|
+
mp4 = get_client().export_timeline(project_id, b.composition)
|
|
298
|
+
return Response(content=mp4, media_type="video/mp4")
|
|
299
|
+
except Exception as e: _err(e)
|
|
300
|
+
|
|
301
|
+
@app.post("/api/v1/publish", tags=["Publishing"], dependencies=AUTH)
|
|
302
|
+
async def publish(b: PR):
|
|
303
|
+
try: return get_client().publish_to_vibes(content_item_id=b.content_item_id, batch_id=b.batch_id,
|
|
304
|
+
caption=b.caption, audio_types=b.audio_types, prompt=b.prompt, image_prompt=b.image_prompt, video_prompt=b.video_prompt)
|
|
305
|
+
except Exception as e: _err(e)
|
|
306
|
+
|
|
307
|
+
@app.post("/api/v1/lipsync", tags=["LipSync"], dependencies=AUTH)
|
|
308
|
+
async def lipsync(b: LR):
|
|
309
|
+
try: return get_client().generate_lipsync(project_id=b.project_id, image_prompt=b.image_prompt,
|
|
310
|
+
script=b.script, audio_url=b.audio_url, audio_duration_ms=b.audio_duration_ms, engine=b.engine,
|
|
311
|
+
ingredients=b.ingredients, aspect_ratio=b.aspect_ratio, music_track=b.music_track, custom_motion_prompt=b.custom_motion_prompt)
|
|
312
|
+
except Exception as e: _err(e)
|
|
313
|
+
|
|
314
|
+
@app.get("/api/v1/music/search", tags=["Music"], dependencies=AUTH)
|
|
315
|
+
async def search_music(q: str = "", limit: int = 30, cursor: Optional[str] = None):
|
|
316
|
+
try: return get_client().search_music_filtered(query=q, limit=limit, cursor=cursor)
|
|
317
|
+
except Exception as e: _err(e)
|
|
318
|
+
|
|
319
|
+
@app.get("/api/v1/moodboards", tags=["Moodboards"], dependencies=AUTH)
|
|
320
|
+
async def moodboards():
|
|
321
|
+
try: return {"moodboards": get_client().list_moodboards()}
|
|
322
|
+
except Exception as e: _err(e)
|
|
323
|
+
|
|
324
|
+
@app.post("/api/v1/utils/parse-midjourney", tags=["Utils"])
|
|
325
|
+
async def parse_mj(b: PM): return VibesClient.parse_midjourney_params(b.prompt)
|
|
326
|
+
|
|
327
|
+
@app.post("/api/v1/utils/validate-prompt", tags=["Utils"])
|
|
328
|
+
async def validate_p(b: PM): return VibesClient.validate_prompt_length(b.prompt)
|
|
329
|
+
|
|
330
|
+
if __name__ == "__main__":
|
|
331
|
+
import uvicorn
|
|
332
|
+
uvicorn.run("server.app:app", host=os.environ.get("HOST","0.0.0.0"), port=int(os.environ.get("PORT","8000")))
|
vibes_api/__init__.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Vibes AI - Unofficial Python API Client
|
|
3
|
+
=======================================
|
|
4
|
+
|
|
5
|
+
A Python wrapper around the vibes.ai private REST API. Supports:
|
|
6
|
+
|
|
7
|
+
- Project management (list / create / get / update / delete / duplicate)
|
|
8
|
+
- Text-to-video generation (midjen-short, 9:16/16:9/1:1, 480p/720p)
|
|
9
|
+
- Text-to-image generation (midjen-base, multiple aspect ratios)
|
|
10
|
+
- **Video extend (auto + manual)** — extend a video by ~5 seconds
|
|
11
|
+
- **Video-to-video editing (v2v)** — re-render with a directive
|
|
12
|
+
- **Image-to-video animate (auto + manual)** — animate a still image
|
|
13
|
+
- **Batch regeneration** — re-roll with same or new prompt
|
|
14
|
+
- **Start/end frame** support (image-to-video with keyframes)
|
|
15
|
+
- **Ingredients** (characters, styles, scenes) — apply, create inline,
|
|
16
|
+
or CRUD via the studio API
|
|
17
|
+
- **Moodboards** — apply style references
|
|
18
|
+
- Image editing (prompt-driven)
|
|
19
|
+
- Prompt enhancement (returns 4 AI-rewritten variations)
|
|
20
|
+
- Lip sync / animation generation
|
|
21
|
+
- Text-to-speech (TTS) with 41 preset voices
|
|
22
|
+
- Media library (list / favorite / delete / download)
|
|
23
|
+
- Generation batch polling & **SSE streaming**
|
|
24
|
+
- Share links
|
|
25
|
+
- Studio ingredients (characters, styles, settings)
|
|
26
|
+
- Timeline chat (streaming AI assistant)
|
|
27
|
+
- Music / audio clip extraction
|
|
28
|
+
- Direct file uploads (image / video / audio / profile picture)
|
|
29
|
+
- Timeline export to MP4 (sync + async SurfGuard)
|
|
30
|
+
- **Real-time sync** (SSE for collaborative editing)
|
|
31
|
+
- Account settings (delete account / media / posts)
|
|
32
|
+
- Quota & upsell info
|
|
33
|
+
- Bug reports & consent
|
|
34
|
+
|
|
35
|
+
Authentication
|
|
36
|
+
--------------
|
|
37
|
+
This client uses cookie-based auth. Provide your `meta_session` cookie value
|
|
38
|
+
(obtained from your browser after logging in at https://vibes.ai). Cookies
|
|
39
|
+
rotate, so refresh when needed.
|
|
40
|
+
|
|
41
|
+
Example
|
|
42
|
+
-------
|
|
43
|
+
from vibes_api import VibesClient, AspectRatio, Resolution, IngredientType
|
|
44
|
+
from vibes_api.ingredients import IngredientRef, CreateIngredient
|
|
45
|
+
|
|
46
|
+
client = VibesClient(meta_session="e60e910a-...-K54E")
|
|
47
|
+
project = client.create_project(name="My first video")
|
|
48
|
+
|
|
49
|
+
# Apply a saved character ingredient
|
|
50
|
+
character = IngredientRef.by_id(
|
|
51
|
+
ingredient_id="800957099700717",
|
|
52
|
+
ingredient_type=IngredientType.CHARACTER,
|
|
53
|
+
name="Valdrin",
|
|
54
|
+
image_url="https://...",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
batch = client.generate_video(
|
|
58
|
+
project_id=project["id"],
|
|
59
|
+
prompt="A serene mountain landscape at sunset",
|
|
60
|
+
aspect_ratio=AspectRatio.LANDSCAPE,
|
|
61
|
+
resolution=Resolution.P720,
|
|
62
|
+
variations=4,
|
|
63
|
+
ingredients=[character],
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Auto-extend the first variation by ~5 seconds
|
|
67
|
+
extended = client.extend_video(
|
|
68
|
+
project_id=project["id"],
|
|
69
|
+
source_video=batch["content"][0],
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
client.download_video(extended["content"][0]["id"], "sunset.mp4")
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
from .client import VibesClient, VibesAPIError
|
|
76
|
+
from .models import (
|
|
77
|
+
AspectRatio,
|
|
78
|
+
EntityType,
|
|
79
|
+
GenerationType,
|
|
80
|
+
ImageModel,
|
|
81
|
+
IngredientType,
|
|
82
|
+
OwnerFilter,
|
|
83
|
+
PromptModel,
|
|
84
|
+
Resolution,
|
|
85
|
+
SyncMode,
|
|
86
|
+
TextOverlayPosition,
|
|
87
|
+
TextOverlayPreset,
|
|
88
|
+
VideoModel,
|
|
89
|
+
)
|
|
90
|
+
from .ingredients import (
|
|
91
|
+
IngredientRef,
|
|
92
|
+
CreateIngredient,
|
|
93
|
+
build_ingredient_payload,
|
|
94
|
+
)
|
|
95
|
+
from .composition import Composition
|
|
96
|
+
|
|
97
|
+
__version__ = "1.3.0"
|
|
98
|
+
__all__ = [
|
|
99
|
+
# Client
|
|
100
|
+
"VibesClient",
|
|
101
|
+
"VibesAPIError",
|
|
102
|
+
# Enums
|
|
103
|
+
"AspectRatio",
|
|
104
|
+
"EntityType",
|
|
105
|
+
"GenerationType",
|
|
106
|
+
"ImageModel",
|
|
107
|
+
"IngredientType",
|
|
108
|
+
"OwnerFilter",
|
|
109
|
+
"PromptModel",
|
|
110
|
+
"Resolution",
|
|
111
|
+
"SyncMode",
|
|
112
|
+
"TextOverlayPosition",
|
|
113
|
+
"TextOverlayPreset",
|
|
114
|
+
"VideoModel",
|
|
115
|
+
# Ingredient helpers
|
|
116
|
+
"IngredientRef",
|
|
117
|
+
"CreateIngredient",
|
|
118
|
+
"build_ingredient_payload",
|
|
119
|
+
# Composition helper
|
|
120
|
+
"Composition",
|
|
121
|
+
]
|