import html from pyrogram import Client, filters from pyrogram.types import CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton from pyrogram.errors import MessageNotModified, MessageIdInvalid from helper.database import codeflixbots from plugins.file_rename import safe_answer # crash-proof query.answer() import pyromod.listen from pyromod.exceptions import ListenerTimeout # ─── Main Settings Menu ──────────────────────────────────────────────────────── @Client.on_message(filters.command("settings") & filters.private) async def settings_menu(client, message): if not await codeflixbots.is_user_authorized(message.from_user.id): return await message.reply_text("❌ You are not authorized to use this bot. Please contact the Owner/Admin to authorize you.") await show_main_menu(client, message.chat.id, message.id, message.from_user.id) async def show_main_menu(client, chat_id, message_id, user_id, cb=None): use_metadata = await codeflixbots.get_metadata(user_id) remnames = await codeflixbots.get_remname_rules(user_id) rename_formats = await codeflixbots.get_rename_formats(user_id) is_reorder = await codeflixbots.get_is_audio_reorder(user_id) is_mediainfo = await codeflixbots.get_is_mediainfo(user_id) is_auto = await codeflixbots.get_is_auto_rename(user_id) auto_merge = await codeflixbots.get_auto_merge(user_id) merge_audio_reorder = await codeflixbots.get_merge_audio_reorder(user_id) keep_audio = await codeflixbots.get_keep_audio(user_id) thumb_size = await codeflixbots.get_thumb_size(user_id) upload_format = await codeflixbots.get_upload_format(user_id) auto_upload = await codeflixbots.get_auto_upload(user_id) _fmt_labels = {"auto": "🔄 Auto", "video": "🎬 Video", "document": "📄 Document"} fmt_label = _fmt_labels.get(upload_format, "🔄 Auto") markup = InlineKeyboardMarkup([ [ InlineKeyboardButton("📝 Caption", callback_data="set_ui_caption"), InlineKeyboardButton("✏️ Prefix", callback_data="set_ui_prefix"), ], [ InlineKeyboardButton("✏️ Suffix", callback_data="set_ui_suffix"), InlineKeyboardButton("🎨 Default Format", callback_data="set_ui_format"), ], [ InlineKeyboardButton(f"📋 Rename Formats ({len(rename_formats)})", callback_data="ui_rename_formats"), ], [ InlineKeyboardButton(f"🔁 RemName Rules ({len(remnames)})", callback_data="set_ui_remnames"), ], [ InlineKeyboardButton("🎵 Audio Order", callback_data="set_ui_audioorder"), InlineKeyboardButton("🎵 Default Track", callback_data="set_ui_audiodefault"), ], [ InlineKeyboardButton(f"{'✅' if use_metadata else '☐'} Audio Metadata", callback_data="toggle_metadata"), InlineKeyboardButton(f"Auto-Rename: {'ON' if is_auto else 'OFF'}", callback_data="toggle_autorename"), ], [ InlineKeyboardButton(f"Reorder: {'ON ✅' if is_reorder else 'OFF ❌'}", callback_data="toggle_audioreorder"), InlineKeyboardButton(f"MediaInfo: {'ON ✅' if is_mediainfo else 'OFF ❌'}", callback_data="toggle_mediainfo"), ], [ InlineKeyboardButton(f"🎧 Keep Audio: {keep_audio if keep_audio else 'ALL ✅'}", callback_data="set_ui_keepaudio"), ], [ InlineKeyboardButton(f"🧩 Auto-Merge (zip): {'ON ✅' if auto_merge else 'OFF ❌'}", callback_data="toggle_automerge"), ], [ InlineKeyboardButton("🎵 Merge Audio Order", callback_data="set_ui_mergeaudioorder"), InlineKeyboardButton(f"Merge Reorder: {'ON ✅' if merge_audio_reorder else 'OFF ❌'}", callback_data="toggle_mergeaudioreorder"), ], [ InlineKeyboardButton(f"📤 Upload As: {fmt_label}", callback_data="cycle_upload_format"), InlineKeyboardButton(f"🚀 Auto Upload: {'ON ✅' if auto_upload else 'OFF ❌'}", callback_data="toggle_autoupload"), ], [ InlineKeyboardButton(f"🖼️ Thumb: {thumb_size}px", callback_data="ui_thumb_layout"), InlineKeyboardButton("✂️ Trim / Split", callback_data="ui_trim_info"), ], [ InlineKeyboardButton("📥 Import Config", callback_data="import_settings"), InlineKeyboardButton("📤 Export Config", callback_data="export_settings"), ], [ InlineKeyboardButton("👁 View All Settings", callback_data="view_settings"), InlineKeyboardButton("❌ Close", callback_data="close"), ] ]) text = ( "⚙️ **Advanced Settings Panel**\n\n" "Tap any button to configure your autofile renaming.\n\n" "*(For Auto Upload mode tweaks like Extract/Merge, Button Mode, setup /autoset)*" ) if cb: try: await cb.message.edit_text(text, reply_markup=markup) except (MessageNotModified, MessageIdInvalid): pass else: await client.send_message(chat_id, text, reply_markup=markup) # ─── Generic Prompt Handler ──────────────────────────────────────────────────── @Client.on_callback_query(filters.regex(r"^set_ui_")) async def handle_set_ui(client, query: CallbackQuery): user_id = query.from_user.id action = query.data.replace("set_ui_", "") if action == "remnames": remnames = await codeflixbots.get_remname_rules(user_id) text = f"**🔁 RemName Rules** ({len(remnames)})\n\n" for i, rule in enumerate(remnames): find = html.escape(rule.get("find", "")) replace = html.escape(rule.get("replace", "")) text += f"`{i+1}. {find}` → `{replace}`\n" if not remnames: text += "_No rules added yet._" markup = InlineKeyboardMarkup([ [InlineKeyboardButton("➕ Add Rule", callback_data="add_remname_rule")], [InlineKeyboardButton("🗑 Clear All", callback_data="clear_remname_rules")], [InlineKeyboardButton("⬅️ Back", callback_data="settings_back")] ]) await query.message.edit_text(text, reply_markup=markup) return # Fetch current values for display current_values = { "caption": await codeflixbots.get_caption(user_id), "prefix": await codeflixbots.get_prefix(user_id), "suffix": await codeflixbots.get_suffix(user_id), "format": await codeflixbots.get_format_template(user_id), "audioorder": await codeflixbots.get_audio_order(user_id), "audiodefault": await codeflixbots.get_audio_default(user_id), "mergeaudioorder": await codeflixbots.get_merge_audio_order(user_id), "keepaudio": await codeflixbots.get_keep_audio(user_id), } current = current_values.get(action) current_text = f"\n\n**Current value:**\n`{html.escape(str(current))}`" if current else "\n\n_Not set yet._" prompts = { "caption": ( f"📝 **Set Custom Caption:**{current_text}\n\n" "**Variables:** `{filename}` (renamed name) | `{original_filename}` (upload name)\n" "`{size}` `{duration}` `{quality}` `{languages}` `{season}` `{episode}`\n\n" "**For Expandable Blockquote:** Use `
content
`\n\n" "Send new caption or `-` to clear.\n\n" "**Tip:** Slash commands such as `/trim` and `/mediainfo` are ignored here, so they will not be treated as caption input.\n" "**Audio Reorder:** At Settings → 🎵 Audio Order, use track indices like `1,0,2` to reorder.\n" "**Metadata:** Set Title/Author/Artist at /metadata command, then toggle metadata ON in settings." ), "prefix": ( f"✏️ **Set Prefix:**{current_text}\n\n" "Use `\\s` for a trailing space. HTML tags supported.\n" "Send new prefix or `-` to clear." ), "suffix": ( f"✏️ **Set Suffix:**{current_text}\n\n" "Send new suffix or `-` to clear." ), "format": ( f"🎨 **Set Default Rename Format:**{current_text}\n\n" "Use `{season}`, `{episode}`, `{quality}` as variables.\n" "Used when no hint-based format matches.\n" "Send new format or `-` to clear." ), "audioorder": ( f"🎵 **Set Audio Order:**{current_text}\n\n" "Comma-separated track indices e.g. `1,0,2`\n" "Send order or `-` to reset." ), "audiodefault": ( f"🎵 **Set Default Audio Track:**{current_text}\n\n" "E.g. `0` = first track, `1` = second.\n" "Send index or `-` to reset." ), "mergeaudioorder": ( f"🎵 **Set Merge Audio Order:**{current_text}\n\n" "Separate from normal Audio Order - only used for **Auto-Merge (zip)** output.\n" "Comma-separated track indices e.g. `1,0,2`\n" "Any track NOT listed here still gets kept (appended after), so nothing is ever removed.\n" "Send order or `-` to reset." ), "keepaudio": ( f"🎧 **Keep Audio Tracks:**{current_text}\n\n" "Keep ONLY the audio tracks you list here. The rest will be permanently removed (deleted) from the output video.\n" "Examples: `0` (first track), `0,2` (first and third tracks), or `Tamil` (match embedded language/name).\n" "Send indexes or language/name values, comma-separated if needed; send `-` to keep all tracks." ), } if action not in prompts: return await query.message.edit_text( prompts[action], reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("❌ Cancel", callback_data="settings_back")]]) ) try: response = await client.listen( chat_id=query.message.chat.id, user_id=user_id, filters=filters.text, timeout=300 ) if response and response.text: text_value = response.text.strip() if text_value.startswith('/'): await response.delete() await show_main_menu(client, query.message.chat.id, query.message.id, user_id, cb=query) return val = None if text_value == "-" else text_value if action == "caption": await codeflixbots.set_caption(user_id, val) elif action == "prefix": await codeflixbots.set_prefix(user_id, val) elif action == "suffix": await codeflixbots.set_suffix(user_id, val) elif action == "format": await codeflixbots.set_format_template(user_id, val) elif action == "audioorder": await codeflixbots.set_audio_order(user_id, val) elif action == "audiodefault": await codeflixbots.set_audio_default(user_id, val) elif action == "mergeaudioorder": await codeflixbots.set_merge_audio_order(user_id, val) elif action == "keepaudio": await codeflixbots.set_keep_audio(user_id, val) await response.delete() await show_main_menu(client, query.message.chat.id, query.message.id, user_id, cb=query) except ListenerTimeout: try: await query.message.edit_text( "⏳ Timed out.", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Back", callback_data="settings_back")]]) ) except (MessageNotModified, MessageIdInvalid): pass # ─── Import / Export Config ─────────────────────────────────────────────────── @Client.on_callback_query(filters.regex(r"^export_settings$")) async def export_settings_cb(client, query: CallbackQuery): user_id = query.from_user.id settings = await codeflixbots.get_all_settings(user_id) if not settings: return await query.answer("No settings found!", show_alert=True) import json import io settings_json = json.dumps(settings, indent=4) file_io = io.BytesIO(settings_json.encode("utf-8")) file_io.name = "RenameBot_Settings.json" await query.message.reply_document( document=file_io, caption="📤 **Your Bot Settings Configuration**\n\nYou can restore these settings anytime using the **Import Config** button in settings.", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("❌ Close", callback_data="close")]]) ) await query.answer() @Client.on_callback_query(filters.regex(r"^import_settings$")) async def import_settings_cb(client, query: CallbackQuery): user_id = query.from_user.id await query.message.edit_text( "📥 **Import Settings**\n\nPlease send me the `RenameBot_Settings.json` file you previously exported.", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("❌ Cancel", callback_data="settings_back")]]) ) try: response = await client.listen( chat_id=query.message.chat.id, user_id=user_id, filters=filters.document, timeout=300 ) if response and response.document: if not response.document.file_name.endswith(".json"): await query.message.edit_text("❌ Invalid file format! Please send a valid JSON config file.", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Back", callback_data="settings_back")]])) await response.delete() return import json import os file_path = await client.download_media(response) try: with open(file_path, "r") as f: data = json.load(f) success = await codeflixbots.set_all_settings(user_id, data) if success: await query.message.edit_text("✅ **Settings imported successfully!**", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("⬅️ Back to Settings", callback_data="settings_back")]])) else: await query.message.edit_text("❌ Failed to update settings in database.", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Back", callback_data="settings_back")]])) except Exception as e: await query.message.edit_text(f"❌ Failed to parse JSON file: {e}", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Back", callback_data="settings_back")]])) finally: if os.path.exists(file_path): os.remove(file_path) await response.delete() except ListenerTimeout: try: await query.message.edit_text( "⏳ Timed out.", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Back", callback_data="settings_back")]]) ) except (MessageNotModified, MessageIdInvalid): pass # ─── Rename Formats Panel ────────────────────────────────────────────────────── @Client.on_callback_query(filters.regex(r"^ui_rename_formats$")) async def show_rename_formats(client, query: CallbackQuery): user_id = query.from_user.id formats = await codeflixbots.get_rename_formats(user_id) text = f"**📋 Rename Formats** ({len(formats)})\n\nHint keyword → Template\n\n" for i, f in enumerate(formats): hint = html.escape(f.get("hint", "")) template = html.escape(f.get("template", "")) text += f"`{i+1}.` 🔑 `{hint}`\n → `{template}`\n\n" if not formats: text += "_No formats added yet._\n\nAdd a format with a hint keyword to auto-detect which template to use based on filename." markup = InlineKeyboardMarkup([ [InlineKeyboardButton("➕ Add Format", callback_data="add_rename_format")], [InlineKeyboardButton("🗑 Remove by Index", callback_data="del_rename_format_prompt"), InlineKeyboardButton("🗑 Clear All", callback_data="clear_rename_formats")], [InlineKeyboardButton("⬅️ Back", callback_data="settings_back")] ]) await query.message.edit_text(text, reply_markup=markup) @Client.on_callback_query(filters.regex(r"^add_rename_format$")) async def add_rename_format_cb(client, query: CallbackQuery): user_id = query.from_user.id await query.message.edit_text( "📋 **Add Rename Format — Step 1/2**\n\n" "Send the **hint keyword** (part of the filename to detect):\n\n" "Example: `One Piece` or `Naruto`", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("❌ Cancel", callback_data="ui_rename_formats")]]) ) try: r1 = await client.listen(chat_id=query.message.chat.id, user_id=user_id, timeout=300) hint = r1.text.strip() await r1.delete() await query.message.edit_text( f"📋 **Add Rename Format — Step 2/2**\n\n" f"Hint: `{html.escape(hint)}`\n\n" "Now send the **rename template**:\n\n" "Use `{season}`, `{episode}`, `{quality}` as variables.\n" "Example: `One Piece S{season}E{episode} {quality} Multi Audio.mkv`", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("❌ Cancel", callback_data="ui_rename_formats")]]) ) r2 = await client.listen(chat_id=query.message.chat.id, user_id=user_id, timeout=300) template = r2.text.strip() await r2.delete() await codeflixbots.add_rename_format(user_id, hint, template) await query.message.edit_text( f"✅ **Format Added!**\n\nHint: `{html.escape(hint)}`\nTemplate: `{html.escape(template)}`", reply_markup=InlineKeyboardMarkup([ [InlineKeyboardButton("📋 View Formats", callback_data="ui_rename_formats")], [InlineKeyboardButton("⬅️ Back to Settings", callback_data="settings_back")] ]) ) except ListenerTimeout: try: await query.message.edit_text( "⏳ Timed out.", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Back", callback_data="settings_back")]]) ) except (MessageNotModified, MessageIdInvalid): pass @Client.on_callback_query(filters.regex(r"^del_rename_format_prompt$")) async def del_rename_format_prompt(client, query: CallbackQuery): user_id = query.from_user.id formats = await codeflixbots.get_rename_formats(user_id) if not formats: return await safe_answer(query, "No formats to remove!", show_alert=True) await query.message.edit_text( f"🗑 **Remove Rename Format**\n\nSend the **index number** to remove (1 to {len(formats)}):", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("❌ Cancel", callback_data="ui_rename_formats")]]) ) try: r = await client.listen(chat_id=query.message.chat.id, user_id=user_id, timeout=60) idx = int(r.text.strip()) - 1 await r.delete() removed = await codeflixbots.remove_rename_format(user_id, idx) if removed: await safe_answer(query, "✅ Format removed!", show_alert=True) else: await safe_answer(query, "⚠️ Invalid index!", show_alert=True) query.data = "ui_rename_formats" await show_rename_formats(client, query) except (ListenerTimeout, ValueError): await safe_answer(query, "Invalid input or timeout.", show_alert=True) query.data = "ui_rename_formats" await show_rename_formats(client, query) @Client.on_callback_query(filters.regex(r"^clear_rename_formats$")) async def clear_rename_formats_cb(client, query: CallbackQuery): await codeflixbots.clear_rename_formats(query.from_user.id) await safe_answer(query, "✅ All rename formats cleared!", show_alert=True) query.data = "ui_rename_formats" await show_rename_formats(client, query) # ─── RemName Rules Panel ─────────────────────────────────────────────────────── @Client.on_callback_query(filters.regex(r"^add_remname_rule$")) async def add_remname_rule_cb(client, query: CallbackQuery): user_id = query.from_user.id await query.message.edit_text( "🔁 **Add RemName Rule**\n\n" "Send in format: `find | replace`\n\n" "Supports Python Regex!\n" "Examples:\n" "• `S\\d+E\\d+ | ` — remove episode tags\n" "• `@OldChannel | @NewChannel` — swap channel\n" "• `KatmovieHD\\.[a-z]+ | ` — remove site name", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("❌ Cancel", callback_data="set_ui_remnames")]]) ) try: response = await client.listen(chat_id=query.message.chat.id, user_id=user_id, timeout=300) if response.text and "|" in response.text: parts = response.text.split("|", 1) rule = {"find": parts[0].strip(), "replace": parts[1].strip()} await codeflixbots.add_remname_rule(user_id, rule) await response.delete() await safe_answer(query, "✅ Rule added!", show_alert=True) else: await safe_answer(query, "⚠️ Invalid format — must contain `|`.", show_alert=True) query.data = "set_ui_remnames" await handle_set_ui(client, query) except ListenerTimeout: await safe_answer(query, "Timed out.", show_alert=True) @Client.on_callback_query(filters.regex(r"^clear_remname_rules$")) async def clear_remnames_cb(client, query: CallbackQuery): await codeflixbots.clear_remname_rules(query.from_user.id) await safe_answer(query, "All RemName rules cleared!", show_alert=True) query.data = "set_ui_remnames" await handle_set_ui(client, query) # ─── Metadata & Rename Toggles ──────────────────────────────────────────────── @Client.on_callback_query(filters.regex(r"^toggle_metadata$")) async def toggle_metadata(client, query: CallbackQuery): user_id = query.from_user.id current = await codeflixbots.get_metadata(user_id) await codeflixbots.set_metadata(user_id, not current) state = "ON ✅" if not current else "OFF ☐" await safe_answer(query, f"Audio Metadata → {state}", show_alert=True) await show_main_menu(client, query.message.chat.id, query.message.id, user_id, cb=query) @Client.on_callback_query(filters.regex(r"^toggle_autorename$")) async def toggle_autorename(client, query: CallbackQuery): user_id = query.from_user.id current = await codeflixbots.get_is_auto_rename(user_id) await codeflixbots.set_is_auto_rename(user_id, not current) state = "ON ✅" if not current else "OFF ☐" await safe_answer(query, f"Auto-Rename Engine → {state}", show_alert=True) await show_main_menu(client, query.message.chat.id, query.message.id, user_id, cb=query) @Client.on_callback_query(filters.regex(r"^toggle_audioreorder$")) async def toggle_audioreorder(client, query: CallbackQuery): user_id = query.from_user.id current = await codeflixbots.get_is_audio_reorder(user_id) await codeflixbots.set_is_audio_reorder(user_id, not current) state = "ON ✅" if not current else "OFF ☐" await safe_answer(query, f"Audio Reorder → {state}", show_alert=True) await show_main_menu(client, query.message.chat.id, query.message.id, user_id, cb=query) @Client.on_callback_query(filters.regex(r"^toggle_mediainfo$")) async def toggle_mediainfo(client, query: CallbackQuery): user_id = query.from_user.id current = await codeflixbots.get_is_mediainfo(user_id) await codeflixbots.set_is_mediainfo(user_id, not current) state = "ON ✅" if not current else "OFF ☐" await safe_answer(query, f"MediaInfo Upload → {state}", show_alert=True) await show_main_menu(client, query.message.chat.id, query.message.id, user_id, cb=query) @Client.on_callback_query(filters.regex(r"^toggle_automerge$")) async def toggle_automerge(client, query: CallbackQuery): user_id = query.from_user.id current = await codeflixbots.get_auto_merge(user_id) await codeflixbots.set_auto_merge(user_id, not current) state = "ON ✅" if not current else "OFF ☐" await safe_answer(query, f"Auto-Merge (zip) → {state}", show_alert=True) await show_main_menu(client, query.message.chat.id, query.message.id, user_id, cb=query) @Client.on_callback_query(filters.regex(r"^toggle_mergeaudioreorder$")) async def toggle_mergeaudioreorder(client, query: CallbackQuery): user_id = query.from_user.id current = await codeflixbots.get_merge_audio_reorder(user_id) await codeflixbots.set_merge_audio_reorder(user_id, not current) state = "ON ✅" if not current else "OFF ☐" await safe_answer(query, f"Merge Audio Reorder → {state}", show_alert=True) await show_main_menu(client, query.message.chat.id, query.message.id, user_id, cb=query) @Client.on_callback_query(filters.regex(r"^cycle_upload_format$")) async def cycle_upload_format(client, query: CallbackQuery): user_id = query.from_user.id current = await codeflixbots.get_upload_format(user_id) cycle = {"auto": "video", "video": "document", "document": "auto"} next_fmt = cycle.get(current, "auto") await codeflixbots.set_upload_format(user_id, next_fmt) labels = {"auto": "🔄 Auto (follow original)", "video": "🎬 Force Video", "document": "📄 Force Document"} await safe_answer(query, f"Upload Format → {labels[next_fmt]}", show_alert=True) await show_main_menu(client, query.message.chat.id, query.message.id, user_id, cb=query) @Client.on_callback_query(filters.regex(r"^toggle_autoupload$")) async def toggle_autoupload(client, query: CallbackQuery): user_id = query.from_user.id current = await codeflixbots.get_auto_upload(user_id) await codeflixbots.set_auto_upload(user_id, not current) state = "ON ✅" if not current else "OFF ☐" await safe_answer(query, f"Auto Upload to Channels → {state}", show_alert=True) await show_main_menu(client, query.message.chat.id, query.message.id, user_id, cb=query) # ─── View All Settings ───────────────────────────────────────────────────────── @Client.on_callback_query(filters.regex(r"^view_settings$")) async def view_settings(client, query: CallbackQuery): user_id = query.from_user.id prefix = await codeflixbots.get_prefix(user_id) suffix = await codeflixbots.get_suffix(user_id) caption = await codeflixbots.get_caption(user_id) fmt = await codeflixbots.get_format_template(user_id) remnames = await codeflixbots.get_remname_rules(user_id) rename_formats = await codeflixbots.get_rename_formats(user_id) audio_order = await codeflixbots.get_audio_order(user_id) audio_default = await codeflixbots.get_audio_default(user_id) metadata = await codeflixbots.get_metadata(user_id) is_auto = await codeflixbots.get_is_auto_rename(user_id) is_reorder = await codeflixbots.get_is_audio_reorder(user_id) is_mediainfo = await codeflixbots.get_is_mediainfo(user_id) auto_merge = await codeflixbots.get_auto_merge(user_id) merge_audio_order = await codeflixbots.get_merge_audio_order(user_id) merge_audio_reorder = await codeflixbots.get_merge_audio_reorder(user_id) keep_audio = await codeflixbots.get_keep_audio(user_id) auto_upload = await codeflixbots.get_auto_upload(user_id) def e(v, max_len=60): if not v: return "_None_" s = html.escape(str(v)) return f"`{s[:max_len]}{'...' if len(s) > max_len else ''}`" text = "⚙️ **Your Settings:**\n\n" text += f"**Auto-Rename:** `{'ON' if is_auto else 'OFF'}`\n" text += f"**Prefix:** {e(prefix)}\n" text += f"**Suffix:** {e(suffix)}\n" text += f"**Caption:** {e(caption)}\n" text += f"**Default Format:** {e(fmt)}\n" text += f"**Audio Order:** {e(audio_order)}\n" text += f"**Default Track:** {e(audio_default)}\n" text += f"**Keep Audio:** {e(keep_audio)}\n" text += f"**Metadata:** `{'ON' if metadata else 'OFF'}`\n" text += f"**Audio Reorder:** `{'ON' if is_reorder else 'OFF'}`\n" text += f"**MediaInfo Upload:** `{'ON' if is_mediainfo else 'OFF'}`\n" text += f"**Auto-Merge (zip):** `{'ON' if auto_merge else 'OFF'}`\n" text += f"**Merge Audio Order:** {e(merge_audio_order)}\n" text += f"**Merge Audio Reorder:** `{'ON' if merge_audio_reorder else 'OFF'}`\n" text += f"**Auto Upload to Channels:** `{'ON' if auto_upload else 'OFF'}`\n" text += f"**RemName Rules:** `{len(remnames)} active`\n" text += f"**Rename Formats:** `{len(rename_formats)} hints configured`\n" await query.message.edit_text( text, reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("⬅️ Back", callback_data="settings_back")]]) ) # ─── Navigation ──────────────────────────────────────────────────────────────── @Client.on_callback_query(filters.regex(r"^settings_back$")) async def settings_back(client, query: CallbackQuery): await show_main_menu(client, query.message.chat.id, query.message.id, query.from_user.id, cb=query) @Client.on_callback_query(filters.regex(r"^close$")) async def close_menu(client, query: CallbackQuery): try: await query.message.delete() except: pass # ─── Thumbnail Layout Picker ─────────────────────────────────────────────── THUMB_SIZES = [ (160, "🟦 Tiny (160px)"), (240, "🟩 Small (240px)"), (320, "🟨 Medium (320px) — Default"), (480, "🟧 Large (480px)"), (720, "🟥 HD (720px)"), ] @Client.on_callback_query(filters.regex(r"^ui_thumb_layout$")) async def ui_thumb_layout(client, query: CallbackQuery): user_id = query.from_user.id current = await codeflixbots.get_thumb_size(user_id) buttons = [] for size, label in THUMB_SIZES: tick = "✅" if current == size else "□" buttons.append([InlineKeyboardButton(f"{tick} {label}", callback_data=f"set_thumb_{size}")]) buttons.append([InlineKeyboardButton("⬅️ Back", callback_data="settings_back")]) await query.message.edit_text( "🖼️ **Thumbnail Size**\n\nChoose the max thumbnail dimension." "\nSmaller = faster upload, larger = crisper preview.", reply_markup=InlineKeyboardMarkup(buttons) ) @Client.on_callback_query(filters.regex(r"^set_thumb_")) async def set_thumb_size_cb(client, query: CallbackQuery): import re m = re.match(r"^set_thumb_(\d+)$", query.data) if not m: return size = int(m.group(1)) await codeflixbots.set_thumb_size(query.from_user.id, size) await safe_answer(query, f"✅ Thumbnail size set to {size}px!", show_alert=True) # Re-render the picker with the updated tick query.data = "ui_thumb_layout" await ui_thumb_layout(client, query) # ─── Trim Info (redirect to /trim) ─────────────────────────────────────────── @Client.on_callback_query(filters.regex(r"^ui_trim_info$")) async def ui_trim_info(client, query: CallbackQuery): await safe_answer(query) await query.message.edit_text( "✂️ **Trim / Split**\n\n" "To trim or split a video/audio file:\n\n" "1️⃣ **Reply** to any video / audio message\n" "2️⃣ Send the command `/trim`\n\n" "The bot will ask whether you want to\n" "• **Split by Parts** — number of equal chunks\n" "• **Trim by Time** — start & end timestamps (`HH:MM:SS`)", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("⬅️ Back", callback_data="settings_back")]]) )