Spaces:
Build error
Build error
Baha Joe commited on
Commit ·
87d591a
1
Parent(s): b6abfcb
Refactor bot.py for robustness and better logging
Browse files
bot.py
CHANGED
|
@@ -1,125 +1,128 @@
|
|
| 1 |
import os
|
| 2 |
-
import
|
| 3 |
-
|
| 4 |
-
from telegram import Update
|
| 5 |
-
from telegram.ext import Application, CommandHandler, MessageHandler,
|
| 6 |
-
from video_utils import
|
| 7 |
|
| 8 |
-
#
|
| 9 |
-
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
-
#
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
await update.message.reply_text(
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
)
|
| 22 |
-
return URL
|
| 23 |
|
| 24 |
-
async def
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
return URL
|
| 29 |
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
async def receive_num_clips(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
| 35 |
-
try:
|
| 36 |
-
count = int(update.message.text)
|
| 37 |
-
if count > 10:
|
| 38 |
-
await update.message.reply_text("⚠️ Let's keep it under 10 clips for performance. Try again.")
|
| 39 |
-
return NUM_CLIPS
|
| 40 |
|
| 41 |
-
context.user_data['num_clips'] = count
|
| 42 |
-
await update.message.reply_text("Got it. How long should each clip be (in seconds)? (e.g., 30)")
|
| 43 |
-
return DURATION
|
| 44 |
-
except ValueError:
|
| 45 |
-
await update.message.reply_text("❌ Please enter a valid number.")
|
| 46 |
-
return NUM_CLIPS
|
| 47 |
-
|
| 48 |
-
async def receive_duration(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
| 49 |
try:
|
| 50 |
-
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
|
|
|
|
|
|
| 59 |
)
|
| 60 |
-
|
| 61 |
-
# Trigger the processing function asynchronously
|
| 62 |
-
# We use create_task so the bot doesn't freeze
|
| 63 |
-
context.application.create_task(process_video(update, context))
|
| 64 |
-
|
| 65 |
-
return ConversationHandler.END
|
| 66 |
-
except ValueError:
|
| 67 |
-
await update.message.reply_text("❌ Please enter a valid number for seconds.")
|
| 68 |
-
return DURATION
|
| 69 |
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
clips = create_clips(video_path, data['num_clips'], data['duration'])
|
| 83 |
-
|
| 84 |
-
# 4. Send Clips
|
| 85 |
-
for clip_path in clips:
|
| 86 |
-
await context.bot.send_video(
|
| 87 |
-
chat_id=user_id,
|
| 88 |
-
video=open(clip_path, 'rb'),
|
| 89 |
-
caption=f"🎥 **{title}**\n\n{tags}",
|
| 90 |
-
read_timeout=60
|
| 91 |
)
|
| 92 |
-
# Clean up individual clip after sending
|
| 93 |
-
os.remove(clip_path)
|
| 94 |
|
| 95 |
-
# Cleanup original video
|
| 96 |
-
os.remove(video_path)
|
| 97 |
-
|
| 98 |
-
await context.bot.send_message(chat_id=user_id, text="✅ All clips sent! Send /start to process another.")
|
| 99 |
-
|
| 100 |
except Exception as e:
|
| 101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
|
| 103 |
-
async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
| 104 |
-
await update.message.reply_text("🚫 Operation cancelled. Send /start to begin again.", reply_markup=ReplyKeyboardRemove())
|
| 105 |
-
return ConversationHandler.END
|
| 106 |
|
| 107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
application = Application.builder().token(TOKEN).build()
|
| 109 |
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
},
|
| 117 |
-
fallbacks=[CommandHandler("cancel", cancel)],
|
| 118 |
-
)
|
| 119 |
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
application.run_polling()
|
|
|
|
| 123 |
|
| 124 |
if __name__ == "__main__":
|
| 125 |
main()
|
|
|
|
| 1 |
import os
|
| 2 |
+
import sys
|
| 3 |
+
import logging
|
| 4 |
+
from telegram import Update
|
| 5 |
+
from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes
|
| 6 |
+
from video_utils import process_video_clip
|
| 7 |
|
| 8 |
+
# Configure logging
|
| 9 |
+
logging.basicConfig(
|
| 10 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
| 11 |
+
level=logging.INFO
|
| 12 |
+
)
|
| 13 |
+
logger = logging.getLogger(__name__)
|
| 14 |
|
| 15 |
+
# --- Configuration ---
|
| 16 |
+
# ⚠️ WARNING: Hardcoding the token is strongly discouraged for security reasons.
|
| 17 |
+
# Use Hugging Face Secrets instead.
|
| 18 |
+
# If you must hardcode it, replace the line below with the token you provided:
|
| 19 |
+
# TOKEN = "8575159633:AAHt8KYNKLrWKID8FOyZipEcPAxZ_zdjgg4"
|
| 20 |
+
#
|
| 21 |
+
# If you are using Hugging Face Secrets (RECOMMENDED):
|
| 22 |
+
TOKEN = os.getenv("BOT_TOKEN")
|
| 23 |
|
| 24 |
+
# Ensure the token is available
|
| 25 |
+
if not TOKEN:
|
| 26 |
+
logger.error("FATAL: BOT_TOKEN is missing. Please set it as a Hugging Face Secret.")
|
| 27 |
+
sys.exit(1)
|
| 28 |
+
|
| 29 |
+
# Ensure necessary directories exist
|
| 30 |
+
DOWNLOAD_DIR = "downloads"
|
| 31 |
+
CLIP_DIR = "clips"
|
| 32 |
+
os.makedirs(DOWNLOAD_DIR, exist_ok=True)
|
| 33 |
+
os.makedirs(CLIP_DIR, exist_ok=True)
|
| 34 |
+
|
| 35 |
+
# --- Command Handlers ---
|
| 36 |
+
|
| 37 |
+
async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
| 38 |
+
"""Sends a welcome message when the /start command is issued."""
|
| 39 |
+
logger.info(f"Received /start command from user {update.effective_user.id}")
|
| 40 |
await update.message.reply_text(
|
| 41 |
+
'👋 Welcome to the AI Telegram Video Clipper Bot!\n\n'
|
| 42 |
+
'Send me a YouTube video link and specify the start and end times '
|
| 43 |
+
'(e.g., `https://youtu.be/example 0:30-1:00`).\n\n'
|
| 44 |
+
'I will generate the clip, transcribe the audio, and send the final video back!'
|
| 45 |
)
|
|
|
|
| 46 |
|
| 47 |
+
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
| 48 |
+
"""Handles messages containing a potential YouTube link."""
|
| 49 |
+
user_message = update.message.text
|
| 50 |
+
user_id = update.effective_user.id
|
|
|
|
| 51 |
|
| 52 |
+
# 1. Simple check for a link and time format
|
| 53 |
+
if "youtu" not in user_message and "0:" not in user_message and "1:" not in user_message:
|
| 54 |
+
logger.info(f"User {user_id} sent non-link message: {user_message[:20]}...")
|
| 55 |
+
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
try:
|
| 58 |
+
# Separate link and time frame
|
| 59 |
+
parts = user_message.split()
|
| 60 |
+
url = parts[0]
|
| 61 |
+
time_range = parts[1] if len(parts) > 1 else None
|
| 62 |
+
|
| 63 |
+
if not time_range:
|
| 64 |
+
await update.message.reply_text(
|
| 65 |
+
"Please specify the clip range (e.g., `1:30-2:00`) after the link."
|
| 66 |
+
)
|
| 67 |
+
return
|
| 68 |
+
|
| 69 |
+
logger.info(f"User {user_id} requested clip: {url} from {time_range}")
|
| 70 |
+
await update.message.reply_text(f"Processing your request for: {time_range}...")
|
| 71 |
|
| 72 |
+
# 2. Call the core processing function
|
| 73 |
+
final_clip_path = await process_video_clip(
|
| 74 |
+
url,
|
| 75 |
+
time_range,
|
| 76 |
+
DOWNLOAD_DIR,
|
| 77 |
+
CLIP_DIR,
|
| 78 |
+
user_id,
|
| 79 |
+
logger # Pass logger for better tracking
|
| 80 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
|
| 82 |
+
if final_clip_path:
|
| 83 |
+
# 3. Send the final video back
|
| 84 |
+
logger.info(f"Sending final clip to user {user_id}: {final_clip_path}")
|
| 85 |
+
await update.message.reply_video(
|
| 86 |
+
video=open(final_clip_path, 'rb'),
|
| 87 |
+
caption=f"✅ Your clip from {time_range} is ready!",
|
| 88 |
+
supports_streaming=True
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
else:
|
| 92 |
+
await update.message.reply_text(
|
| 93 |
+
"❌ Error processing your clip. Please check the URL and time format (e.g., 0:30-1:00) and ensure the video is available."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
)
|
|
|
|
|
|
|
| 95 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
except Exception as e:
|
| 97 |
+
logger.error(f"An unexpected error occurred during processing for user {user_id}: {e}", exc_info=True)
|
| 98 |
+
await update.message.reply_text("An unexpected error occurred. Please check the format and try again.")
|
| 99 |
+
|
| 100 |
+
finally:
|
| 101 |
+
# 4. Clean up files after processing
|
| 102 |
+
# Ensure your video_utils handles cleanup of temporary files (Crucial for free tier)
|
| 103 |
+
pass
|
| 104 |
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
+
# --- Main Application Runner ---
|
| 107 |
+
|
| 108 |
+
def main() -> None:
|
| 109 |
+
"""Start the bot."""
|
| 110 |
+
logger.info("Starting Telegram Application...")
|
| 111 |
+
|
| 112 |
+
# 1. Build the application
|
| 113 |
application = Application.builder().token(TOKEN).build()
|
| 114 |
|
| 115 |
+
# 2. Register handlers
|
| 116 |
+
application.add_handler(CommandHandler("start", start_command))
|
| 117 |
+
application.add_handler(MessageHandler(
|
| 118 |
+
filters.TEXT & ~filters.COMMAND,
|
| 119 |
+
handle_message
|
| 120 |
+
))
|
|
|
|
|
|
|
|
|
|
| 121 |
|
| 122 |
+
# 3. Start polling
|
| 123 |
+
logger.info("Bot is ready. Starting polling...")
|
| 124 |
+
application.run_polling(allowed_updates=Update.ALL_TYPES)
|
| 125 |
+
logger.info("Bot polling stopped.")
|
| 126 |
|
| 127 |
if __name__ == "__main__":
|
| 128 |
main()
|