Changes:
1. Frontend multi-upload:
- File input now has 'multiple' attribute, drag-drop accepts multiple
- File queue list with per-file artist/title preview + remove button
- 'Pošlji vse' uploads sequentially (one at a time to avoid network saturation)
- Each file gets same batch_id for Telegram batch summary
- After upload, queue clears, jobs appear in right sidebar
2. Backend queue worker:
- New _queue_worker() background thread processes 'queued' jobs sequentially
- Only 1 job at a time to keep openclaw stable (avoid CPU/RAM thrash)
- FIFO order by created_at
- Auto-starts on app startup after job resume
3. Job submission flow change:
- /api/process and /api/youtube no longer call background.add_task directly
- Just mark status='queued', queue worker picks up
- This means upload completes fast, processing happens in background
- User can close browser, jobs continue
4. Telegram notifications (FOLX Alerts bot):
- Per-job: 'Reel pripravljen: Lady Gaga - Abracadabra (29s, 30 MB)'
- Per-job failed: 'Reel ni uspel: <name> + error message'
- Batch summary: 'Batch končan: 10/10 reels pripravljeni' (only if >1 in batch)
- Uses existing TELEGRAM_TOKEN + TELEGRAM_CHAT_ID env vars
- app/telegram.py module with notify_job_done(), notify_job_failed(),
notify_batch_complete()
5. batch_id field:
- Added to Job model + StartJobIn pydantic
- Saved during upload + process
- Used to count batch progress and trigger summary notification
User experience:
- Drag 20 videos at once
- Click 'Pošlji'
- Close browser, go grab coffee
- Telegram sends 'Reel pripravljen' for each
- After all done: 'Batch končan: 20/20 reels pripravljeni' summary
- Open app to download all
120 lines
3.6 KiB
Python
120 lines
3.6 KiB
Python
"""
|
|
telegram.py — Telegram bot helper za reels-app.
|
|
|
|
Pošilja obvestila o končanih jobih, batch summary, napakah.
|
|
Credentials se preberejo iz env vars (TELEGRAM_TOKEN, TELEGRAM_CHAT_ID).
|
|
"""
|
|
import os
|
|
import urllib.request
|
|
import urllib.parse
|
|
import json
|
|
|
|
|
|
def send_message(text, parse_mode="Markdown", disable_notification=False):
|
|
"""Pošlji sporočilo na Telegram. Vrne True ob uspehu, False ob napaki.
|
|
|
|
text: ne sme biti daljši od 4096 znakov.
|
|
parse_mode: 'Markdown' ali 'HTML' ali None.
|
|
disable_notification: True za tiho obvestilo (brez zvonca).
|
|
"""
|
|
token = os.environ.get("TELEGRAM_TOKEN")
|
|
chat_id = os.environ.get("TELEGRAM_CHAT_ID")
|
|
if not token or not chat_id:
|
|
return False
|
|
|
|
# Telegram limit: 4096 chars
|
|
if len(text) > 4090:
|
|
text = text[:4087] + "..."
|
|
|
|
data_dict = {
|
|
"chat_id": chat_id,
|
|
"text": text,
|
|
}
|
|
if parse_mode:
|
|
data_dict["parse_mode"] = parse_mode
|
|
if disable_notification:
|
|
data_dict["disable_notification"] = "true"
|
|
|
|
data = urllib.parse.urlencode(data_dict).encode()
|
|
req = urllib.request.Request(
|
|
f"https://api.telegram.org/bot{token}/sendMessage",
|
|
data=data,
|
|
method="POST",
|
|
)
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=10) as resp:
|
|
result = json.loads(resp.read().decode())
|
|
return result.get("ok", False)
|
|
except Exception as e:
|
|
print(f"⚠️ Telegram send failed: {e}", flush=True)
|
|
return False
|
|
|
|
|
|
def notify_job_done(job, base_url="https://reels.biba.live"):
|
|
"""Obvesti o končanem reelu."""
|
|
artist = job.get("parsed_artist", "")
|
|
title = job.get("parsed_title", "")
|
|
job_id = job.get("id", "")
|
|
|
|
if artist and title:
|
|
name = f"*{artist} — {title}*"
|
|
elif title:
|
|
name = f"*{title}*"
|
|
else:
|
|
name = f"`{job_id}`"
|
|
|
|
duration = job.get("duration", 0)
|
|
output_size_mb = job.get("output_size_mb") or round((job.get("output_size", 0) / 1024 / 1024), 1)
|
|
|
|
text = (
|
|
f"✅ Reel pripravljen\n\n"
|
|
f"{name}\n"
|
|
f"⏱ {duration:.0f}s · 📦 {output_size_mb} MB\n\n"
|
|
f"[Predogled & Download]({base_url})"
|
|
)
|
|
return send_message(text)
|
|
|
|
|
|
def notify_job_failed(job, error_msg=""):
|
|
"""Obvesti o neuspehu joba."""
|
|
artist = job.get("parsed_artist", "")
|
|
title = job.get("parsed_title", "")
|
|
fname = job.get("filename", "")
|
|
|
|
if artist and title:
|
|
name = f"*{artist} — {title}*"
|
|
elif fname:
|
|
name = f"`{fname}`"
|
|
else:
|
|
name = f"`{job.get('id', '?')}`"
|
|
|
|
err_short = (error_msg or "neznana napaka")[:200]
|
|
text = f"⚠️ Reel ni uspel\n\n{name}\n\n```\n{err_short}\n```"
|
|
return send_message(text)
|
|
|
|
|
|
def notify_batch_complete(batch_id, total, succeeded, failed, base_url="https://reels.biba.live"):
|
|
"""Obvesti o končanem batch-u (več jobov hkrati)."""
|
|
if total <= 1:
|
|
return False # ne pošiljaj batch summary za en sam job
|
|
|
|
if failed == 0:
|
|
emoji = "🎉"
|
|
head = f"Batch končan: vsi {total} reelov pripravljeni"
|
|
else:
|
|
emoji = "✅"
|
|
head = f"Batch končan: {succeeded}/{total} uspelih"
|
|
if failed > 0:
|
|
head += f", {failed} neuspelih"
|
|
|
|
text = f"{emoji} {head}\n\n[Vsi reels →]({base_url})"
|
|
return send_message(text)
|
|
|
|
|
|
def notify_batch_started(batch_id, total):
|
|
"""Obvesti o začetku batch-a (samo če > 1 job)."""
|
|
if total <= 1:
|
|
return False
|
|
text = f"🎬 Batch za {total} datotek dodan v vrsto…"
|
|
return send_message(text, disable_notification=True)
|