Files changed (1) hide show
  1. main.py +73 -72
main.py CHANGED
@@ -8,6 +8,12 @@ import gradio as gr
8
  from utils.logger import logger
9
  from utils.job_queue import start_worker, create_job, get_job
10
 
 
 
 
 
 
 
11
  from utils.transcription import transcribe_video
12
  from utils.srt import generate_srt
13
  from utils.render import render_subtitles
@@ -19,51 +25,54 @@ from utils.platform import adapt_platform
19
  from utils.persona import predict_audience
20
  from utils.clipper import create_clips
21
 
22
- from ingestion.resolver import resolve_input
23
  from utils.autonomous_engine import run_autonomous_engine
24
 
25
- # ---------- Publisher ----------
26
- from publisher.publisher_scheduler_ai import autonomous_publish
27
- from publisher.metadata import generate_metadata
28
- from publisher.hashtags import generate_hashtags
29
- from publisher.thumbnail import generate_thumbnail
30
- from publisher.scheduler import schedule_post
31
- from publisher.bulk import execute as bulk_execute
32
 
33
- from publisher.platforms.tiktok import publish_tiktok
34
- from publisher.platforms.reels import publish_reels
35
- from publisher.platforms.shorts import publish_shorts
36
- from publisher.platforms.facebook import publish_facebook
 
 
 
 
 
 
37
 
38
 
39
- # =====================================================
40
- # INIT
41
- # =====================================================
42
 
43
- app = FastAPI(title="Basyx Whisper V9 Autonomous Content Operator")
44
 
45
  UPLOAD_DIR = "jobs"
46
  os.makedirs(UPLOAD_DIR, exist_ok=True)
47
 
48
  start_worker()
49
 
 
 
 
50
 
51
- # =====================================================
 
 
 
 
 
 
52
  # TASK NORMALIZER
53
- # =====================================================
54
 
55
  VALID_TASKS = {
56
  "autonomous",
57
  "auto-publish",
58
- "publish-tiktok",
59
- "publish-reels",
60
- "publish-shorts",
61
- "publish-facebook",
62
  "generate-metadata",
63
- "generate-hashtags",
64
  "generate-thumbnail",
65
  "schedule-post",
66
- "bulk-publish",
67
  "transcribe",
68
  "subtitles",
69
  "render",
@@ -73,6 +82,7 @@ VALID_TASKS = {
73
  "batch",
74
  }
75
 
 
76
  def normalize_task(task: str):
77
  task = task.lower().replace("_", "-")
78
  if task not in VALID_TASKS:
@@ -80,78 +90,63 @@ def normalize_task(task: str):
80
  return task
81
 
82
 
83
- # =====================================================
84
  # EXECUTION ENGINE
85
- # =====================================================
86
 
87
  async def execute_task(
88
  video_path: str | None,
89
  task: str,
90
  payload: dict | None = None,
91
- webhook=None
92
  ):
93
 
 
 
94
  logger.info(f"[TASK] {task}")
95
 
96
- # ---------- BULK ----------
97
  if task == "bulk-publish":
98
- return await bulk_execute(payload or {}), None
99
 
100
- # ---------- AUTONOMOUS ----------
101
  if task == "autonomous":
102
  result = await asyncio.to_thread(
103
  run_autonomous_engine,
104
- video_path
105
  )
106
- best = result.get("best_clip")
107
- return result, best.get("clip") if best else None
108
 
109
  if task == "auto-publish":
 
110
  autonomous = await asyncio.to_thread(
111
  run_autonomous_engine,
112
- video_path
113
  )
114
 
115
- publish_result = await autonomous_publish(
116
  variants=autonomous["all_variants"]
117
  )
118
 
119
  return publish_result, None
120
 
121
- # ---------- METADATA ----------
 
 
 
 
 
 
 
 
122
  if task == "generate-metadata":
123
  return generate_metadata(video_path), None
124
 
125
- if task == "generate-hashtags":
126
- return generate_hashtags(video_path), None
127
-
128
  if task == "generate-thumbnail":
129
  thumb = generate_thumbnail(video_path)
130
  return {"thumbnail": thumb}, thumb
131
 
132
- # ---------- PLATFORM PUBLISH ----------
133
- publish_payload = {
134
- "video_path": video_path,
135
- **(payload or {})
136
- }
137
-
138
- if task == "publish-tiktok":
139
- return await publish_tiktok(publish_payload), None
140
-
141
- if task == "publish-reels":
142
- return await publish_reels(publish_payload), None
143
-
144
- if task == "publish-shorts":
145
- return await publish_shorts(publish_payload), None
146
-
147
- if task == "publish-facebook":
148
- return await publish_facebook(publish_payload), None
149
-
150
- # ---------- SCHEDULER ----------
151
- if task == "schedule-post":
152
- return schedule_post(payload or {}), None
153
-
154
- # ---------- PIPELINE ----------
155
  if task == "batch":
156
  job_id = create_job(video_path, webhook=webhook)
157
  return {"status": "queued", "job_id": job_id}, None
@@ -165,6 +160,7 @@ async def execute_task(
165
  return {"srt": generate_srt(words)}, None
166
 
167
  if task == "render":
 
168
  words = await asyncio.to_thread(transcribe_video, video_path)
169
  srt = generate_srt(words)
170
 
@@ -183,18 +179,23 @@ async def execute_task(
183
  return {"status": "render_complete"}, output
184
 
185
  if task == "highlights":
 
186
  words = await asyncio.to_thread(transcribe_video, video_path)
187
  highlights = detect_highlights(words)
188
  clips = create_clips(video_path, highlights)
 
189
  return {"clips_created": len(clips)}, clips[0]
190
 
191
  if task == "viral-score":
 
192
  words = await asyncio.to_thread(transcribe_video, video_path)
193
  segments = detect_highlights(words) or []
194
  scores = [score_clip(s) for s in segments]
 
195
  return {"scores": scores}, None
196
 
197
  if task == "strategy":
 
198
  words = await asyncio.to_thread(transcribe_video, video_path)
199
 
200
  script = rewrite_script(words)
@@ -214,9 +215,9 @@ async def execute_task(
214
  raise Exception("Task execution failed")
215
 
216
 
217
- # =====================================================
218
  # UNIVERSAL EXECUTE ROUTER
219
- # =====================================================
220
 
221
  @app.post("/execute/{task_name}")
222
  async def execute_router(
@@ -237,10 +238,10 @@ async def execute_router(
237
  if request.headers.get("content-type", "").startswith("application/json"):
238
  payload = await request.json()
239
 
240
- # ---- Resolve video only if needed ----
241
  video_path = None
242
 
243
- if task != "bulk-publish" and (
244
  file or url_input or source
245
  ):
246
  input_source = url_input or source
@@ -268,13 +269,13 @@ async def execute_router(
268
  return JSONResponse({"error": str(e)}, status_code=500)
269
 
270
 
271
- # =====================================================
272
  # HEALTH
273
- # =====================================================
274
 
275
  @app.get("/api/health")
276
  def api_health():
277
- return {"status": "online", "version": "V9.3"}
278
 
279
 
280
  @app.get("/api/status/{job_id}")
@@ -282,9 +283,9 @@ def api_status(job_id: str):
282
  return get_job(job_id) or {"error": "Job not found"}
283
 
284
 
285
- # =====================================================
286
  # GRADIO UI
287
- # =====================================================
288
 
289
  async def ui_handler(video, task, webhook, url_input):
290
 
@@ -310,7 +311,7 @@ async def ui_handler(video, task, webhook, url_input):
310
 
311
  with gr.Blocks() as demo:
312
 
313
- gr.Markdown("# Basyx Whisper V9 Autonomous Operator")
314
 
315
  video_input = gr.Video()
316
  url_input = gr.Textbox(label="Video URL")
 
8
  from utils.logger import logger
9
  from utils.job_queue import start_worker, create_job, get_job
10
 
11
+ from ingestion.resolver import resolve_input
12
+
13
+ # ==============================
14
+ # CORE PIPELINE
15
+ # ==============================
16
+
17
  from utils.transcription import transcribe_video
18
  from utils.srt import generate_srt
19
  from utils.render import render_subtitles
 
25
  from utils.persona import predict_audience
26
  from utils.clipper import create_clips
27
 
 
28
  from utils.autonomous_engine import run_autonomous_engine
29
 
 
 
 
 
 
 
 
30
 
31
+ # ==============================
32
+ # PUBLISHER V10
33
+ # ==============================
34
+
35
+ from publisher.publisher_ai import autonomous_loop
36
+ from publisher.scheduler_engine import init_scheduler
37
+ from publisher.platform_dispatcher import dispatch_publish
38
+ from publisher.bulk import execute as bulk_execute
39
+ from publisher.metadata_engine import generate_metadata
40
+ from publisher.thumbnail_engine import generate_thumbnail
41
 
42
 
43
+ # ==============================
44
+ # INIT APP
45
+ # ==============================
46
 
47
+ app = FastAPI(title="Basyx Whisper V10 Autonomous Content Operator")
48
 
49
  UPLOAD_DIR = "jobs"
50
  os.makedirs(UPLOAD_DIR, exist_ok=True)
51
 
52
  start_worker()
53
 
54
+ # ---- Start Autonomous Systems ----
55
+ init_scheduler()
56
+
57
 
58
+ @app.on_event("startup")
59
+ async def startup_event():
60
+ logger.info("Starting Autonomous Publisher Brain V10")
61
+ asyncio.create_task(autonomous_loop())
62
+
63
+
64
+ # ==============================
65
  # TASK NORMALIZER
66
+ # ==============================
67
 
68
  VALID_TASKS = {
69
  "autonomous",
70
  "auto-publish",
71
+ "publish",
72
+ "bulk-publish",
 
 
73
  "generate-metadata",
 
74
  "generate-thumbnail",
75
  "schedule-post",
 
76
  "transcribe",
77
  "subtitles",
78
  "render",
 
82
  "batch",
83
  }
84
 
85
+
86
  def normalize_task(task: str):
87
  task = task.lower().replace("_", "-")
88
  if task not in VALID_TASKS:
 
90
  return task
91
 
92
 
93
+ # ==============================
94
  # EXECUTION ENGINE
95
+ # ==============================
96
 
97
  async def execute_task(
98
  video_path: str | None,
99
  task: str,
100
  payload: dict | None = None,
101
+ webhook=None,
102
  ):
103
 
104
+ payload = payload or {}
105
+
106
  logger.info(f"[TASK] {task}")
107
 
108
+ # ---------------- BULK ----------------
109
  if task == "bulk-publish":
110
+ return await bulk_execute(payload), None
111
 
112
+ # ---------------- AUTONOMOUS ----------------
113
  if task == "autonomous":
114
  result = await asyncio.to_thread(
115
  run_autonomous_engine,
116
+ video_path,
117
  )
118
+ return result, None
 
119
 
120
  if task == "auto-publish":
121
+
122
  autonomous = await asyncio.to_thread(
123
  run_autonomous_engine,
124
+ video_path,
125
  )
126
 
127
+ publish_result = await dispatch_publish(
128
  variants=autonomous["all_variants"]
129
  )
130
 
131
  return publish_result, None
132
 
133
+ # ---------------- PUBLISH ----------------
134
+ if task == "publish":
135
+ result = await dispatch_publish(
136
+ video_path=video_path,
137
+ payload=payload,
138
+ )
139
+ return result, None
140
+
141
+ # ---------------- METADATA ----------------
142
  if task == "generate-metadata":
143
  return generate_metadata(video_path), None
144
 
 
 
 
145
  if task == "generate-thumbnail":
146
  thumb = generate_thumbnail(video_path)
147
  return {"thumbnail": thumb}, thumb
148
 
149
+ # ---------------- PIPELINE ----------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  if task == "batch":
151
  job_id = create_job(video_path, webhook=webhook)
152
  return {"status": "queued", "job_id": job_id}, None
 
160
  return {"srt": generate_srt(words)}, None
161
 
162
  if task == "render":
163
+
164
  words = await asyncio.to_thread(transcribe_video, video_path)
165
  srt = generate_srt(words)
166
 
 
179
  return {"status": "render_complete"}, output
180
 
181
  if task == "highlights":
182
+
183
  words = await asyncio.to_thread(transcribe_video, video_path)
184
  highlights = detect_highlights(words)
185
  clips = create_clips(video_path, highlights)
186
+
187
  return {"clips_created": len(clips)}, clips[0]
188
 
189
  if task == "viral-score":
190
+
191
  words = await asyncio.to_thread(transcribe_video, video_path)
192
  segments = detect_highlights(words) or []
193
  scores = [score_clip(s) for s in segments]
194
+
195
  return {"scores": scores}, None
196
 
197
  if task == "strategy":
198
+
199
  words = await asyncio.to_thread(transcribe_video, video_path)
200
 
201
  script = rewrite_script(words)
 
215
  raise Exception("Task execution failed")
216
 
217
 
218
+ # ==============================
219
  # UNIVERSAL EXECUTE ROUTER
220
+ # ==============================
221
 
222
  @app.post("/execute/{task_name}")
223
  async def execute_router(
 
238
  if request.headers.get("content-type", "").startswith("application/json"):
239
  payload = await request.json()
240
 
241
+ # ---- UNIVERSAL INPUT LAYER ----
242
  video_path = None
243
 
244
+ if task not in ["bulk-publish"] and (
245
  file or url_input or source
246
  ):
247
  input_source = url_input or source
 
269
  return JSONResponse({"error": str(e)}, status_code=500)
270
 
271
 
272
+ # ==============================
273
  # HEALTH
274
+ # ==============================
275
 
276
  @app.get("/api/health")
277
  def api_health():
278
+ return {"status": "online", "version": "V10"}
279
 
280
 
281
  @app.get("/api/status/{job_id}")
 
283
  return get_job(job_id) or {"error": "Job not found"}
284
 
285
 
286
+ # ==============================
287
  # GRADIO UI
288
+ # ==============================
289
 
290
  async def ui_handler(video, task, webhook, url_input):
291
 
 
311
 
312
  with gr.Blocks() as demo:
313
 
314
+ gr.Markdown("# Basyx Whisper V10 Autonomous Operator")
315
 
316
  video_input = gr.Video()
317
  url_input = gr.Textbox(label="Video URL")