Spaces:
Running
Running
SilverElixir commited on
Commit ·
4e6721e
1
Parent(s): a348a03
TikTok UX: localized slideshow status (25 langs), audio duration probe, status lives through music fetch
Browse files- bot.py +2 -0
- lumen_lang.py +27 -0
- lumen_tiktok.py +20 -0
- lumen_tiktok_flow.py +14 -8
- tests/test_bot_tiktok.py +128 -0
bot.py
CHANGED
|
@@ -692,6 +692,7 @@ __all__ = [
|
|
| 692 |
"_tiktok_music_page_id",
|
| 693 |
"_write_mp3_tags",
|
| 694 |
"_download_url_bin",
|
|
|
|
| 695 |
"_probe_video_dimensions",
|
| 696 |
"_generate_video_thumbnail",
|
| 697 |
"_probe_and_thumbnail_from_bytes",
|
|
@@ -968,6 +969,7 @@ from lumen_tiktok import (
|
|
| 968 |
_tiktok_music_page_id,
|
| 969 |
_write_mp3_tags,
|
| 970 |
_download_url_bin,
|
|
|
|
| 971 |
_probe_video_dimensions,
|
| 972 |
_generate_video_thumbnail,
|
| 973 |
_communicate_process,
|
|
|
|
| 692 |
"_tiktok_music_page_id",
|
| 693 |
"_write_mp3_tags",
|
| 694 |
"_download_url_bin",
|
| 695 |
+
"_probe_audio_duration",
|
| 696 |
"_probe_video_dimensions",
|
| 697 |
"_generate_video_thumbnail",
|
| 698 |
"_probe_and_thumbnail_from_bytes",
|
|
|
|
| 969 |
_tiktok_music_page_id,
|
| 970 |
_write_mp3_tags,
|
| 971 |
_download_url_bin,
|
| 972 |
+
_probe_audio_duration,
|
| 973 |
_probe_video_dimensions,
|
| 974 |
_generate_video_thumbnail,
|
| 975 |
_communicate_process,
|
lumen_lang.py
CHANGED
|
@@ -591,6 +591,14 @@ STRINGS: dict[str, dict[str, str]] = {
|
|
| 591 |
"ru": "Музыка из TikTok",
|
| 592 |
"uk": "Музика з TikTok",
|
| 593 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 594 |
# ── Статусы ──
|
| 595 |
"status_generating_image": {
|
| 596 |
"be": "Генерую малюнак",
|
|
@@ -739,6 +747,7 @@ PACK_PT: dict[str, str] = {
|
|
| 739 |
"tiktok_generic_fail": "Não consegui baixar este vídeo ou apresentação do TikTok. Tente outro link ou repita um pouco mais tarde.",
|
| 740 |
"tiktok_author": "Autor do TikTok",
|
| 741 |
"tiktok_music": "Música do TikTok",
|
|
|
|
| 742 |
"status_generating_image": "Gerando imagem",
|
| 743 |
"status_voicing": "Convertendo texto em voz",
|
| 744 |
"status_taking_longer": "Isso vai demorar um pouco mais…",
|
|
@@ -819,6 +828,7 @@ PACK_AR: dict[str, str] = {
|
|
| 819 |
"tiktok_generic_fail": "تعذر تنزيل هذا الفيديو أو عرض الشرائح من TikTok. جرب رابطًا آخر أو أعد المحاولة بعد قليل.",
|
| 820 |
"tiktok_author": "مؤلف TikTok",
|
| 821 |
"tiktok_music": "موسيقى من TikTok",
|
|
|
|
| 822 |
"status_generating_image": "جارٍ توليد الصورة",
|
| 823 |
"status_voicing": "جارٍ تحويل النص إلى صوت",
|
| 824 |
"status_taking_longer": "سيستغرق هذا وقتًا أطول قليلًا…",
|
|
@@ -899,6 +909,7 @@ PACK_TR: dict[str, str] = {
|
|
| 899 |
"tiktok_generic_fail": "Bu video veya slayt TikTok'tan indirilemedi. Başka bağlantı dene veya biraz sonra tekrarla.",
|
| 900 |
"tiktok_author": "TikTok yazarı",
|
| 901 |
"tiktok_music": "TikTok müziği",
|
|
|
|
| 902 |
"status_generating_image": "Görsel oluşturuluyor",
|
| 903 |
"status_voicing": "Metin seslendiriliyor",
|
| 904 |
"status_taking_longer": "Bu biraz daha uzun sürecek…",
|
|
@@ -979,6 +990,7 @@ PACK_DE: dict[str, str] = {
|
|
| 979 |
"tiktok_generic_fail": "Dieses Video bzw. diese Slideshow ließ sich nicht von TikTok laden. Anderen Link versuchen oder etwas später wiederholen.",
|
| 980 |
"tiktok_author": "TikTok-Autor",
|
| 981 |
"tiktok_music": "Musik von TikTok",
|
|
|
|
| 982 |
"status_generating_image": "Bild wird erzeugt",
|
| 983 |
"status_voicing": "Text wird vertont",
|
| 984 |
"status_taking_longer": "Das dauert etwas länger…",
|
|
@@ -1059,6 +1071,7 @@ PACK_FR: dict[str, str] = {
|
|
| 1059 |
"tiktok_generic_fail": "Impossible de télécharger cette vidéo ou ce diaporama depuis TikTok. Essaie un autre lien ou réessaie un peu plus tard.",
|
| 1060 |
"tiktok_author": "Auteur TikTok",
|
| 1061 |
"tiktok_music": "Musique de TikTok",
|
|
|
|
| 1062 |
"status_generating_image": "Génération de l'image",
|
| 1063 |
"status_voicing": "Conversion du texte en voix",
|
| 1064 |
"status_taking_longer": "Ça va prendre un peu plus de temps…",
|
|
@@ -1139,6 +1152,7 @@ PACK_IT: dict[str, str] = {
|
|
| 1139 |
"tiktok_generic_fail": "Impossibile scaricare questo video o slideshow da TikTok. Prova un altro link o ripeti tra poco.",
|
| 1140 |
"tiktok_author": "Autore TikTok",
|
| 1141 |
"tiktok_music": "Musica di TikTok",
|
|
|
|
| 1142 |
"status_generating_image": "Generazione dell'immagine",
|
| 1143 |
"status_voicing": "Conversione del testo in voce",
|
| 1144 |
"status_taking_longer": "Ci vorrà un po' più di tempo…",
|
|
@@ -1219,6 +1233,7 @@ PACK_HI: dict[str, str] = {
|
|
| 1219 |
"tiktok_generic_fail": "यह वीडियो या स्लाइडशो TikTok से डाउनलोड नहीं हो सका। दूसरा लिंक आज़माओ या थोड़ी देर बाद दोहराओ।",
|
| 1220 |
"tiktok_author": "TikTok लेखक",
|
| 1221 |
"tiktok_music": "TikTok संगीत",
|
|
|
|
| 1222 |
"status_generating_image": "चित्र बनाया जा रहा है",
|
| 1223 |
"status_voicing": "टेक्स्ट को आवाज़ दी जा रही है",
|
| 1224 |
"status_taking_longer": "इसमें थोड़ा और समय लगेगा…",
|
|
@@ -1299,6 +1314,7 @@ PACK_ID: dict[str, str] = {
|
|
| 1299 |
"tiktok_generic_fail": "Tidak bisa mengunduh video atau slideshow ini dari TikTok. Coba tautan lain atau ulangi sedikit lagi.",
|
| 1300 |
"tiktok_author": "Penulis TikTok",
|
| 1301 |
"tiktok_music": "Musik dari TikTok",
|
|
|
|
| 1302 |
"status_generating_image": "Membuat gambar",
|
| 1303 |
"status_voicing": "Mengucapkan teks",
|
| 1304 |
"status_taking_longer": "Ini akan memakan waktu sedikit lebih lama…",
|
|
@@ -1379,6 +1395,7 @@ PACK_MS: dict[str, str] = {
|
|
| 1379 |
"tiktok_generic_fail": "Tidak dapat memuat turun video atau slaid ini dari TikTok. Cuba pautan lain atau ulangi sedikit lagi.",
|
| 1380 |
"tiktok_author": "Pengarang TikTok",
|
| 1381 |
"tiktok_music": "Muzik dari TikTok",
|
|
|
|
| 1382 |
"status_generating_image": "Menjana gambar",
|
| 1383 |
"status_voicing": "Mengucapkan teks",
|
| 1384 |
"status_taking_longer": "Ini akan mengambil masa sedikit lebih lama…",
|
|
@@ -1459,6 +1476,7 @@ PACK_VI: dict[str, str] = {
|
|
| 1459 |
"tiktok_generic_fail": "Không tải được video hay slideshow này từ TikTok. Thử liên kết khác hoặc lặp lại sau một lúc.",
|
| 1460 |
"tiktok_author": "Tác giả TikTok",
|
| 1461 |
"tiktok_music": "Nhạc từ TikTok",
|
|
|
|
| 1462 |
"status_generating_image": "Đang tạo ảnh",
|
| 1463 |
"status_voicing": "Đang đọc văn bản",
|
| 1464 |
"status_taking_longer": "Việc này sẽ mất lâu hơn một chút…",
|
|
@@ -1539,6 +1557,7 @@ PACK_TH: dict[str, str] = {
|
|
| 1539 |
"tiktok_generic_fail": "ดาวน์โหลดวิดีโอหรือสไลด์โชว์นี้จาก TikTok ไม่ได้ ลองลิงก์อื่นหรือทำซ้ำอีกสักครู่",
|
| 1540 |
"tiktok_author": "ผู้เขียน TikTok",
|
| 1541 |
"tiktok_music": "เพลงจาก TikTok",
|
|
|
|
| 1542 |
"status_generating_image": "กำลังสร้างรูปภาพ",
|
| 1543 |
"status_voicing": "กำลังอ่านข้อความ",
|
| 1544 |
"status_taking_longer": "อันนี้จะใช้เวลานานกว่านิดหน่อย…",
|
|
@@ -1619,6 +1638,7 @@ PACK_FA: dict[str, str] = {
|
|
| 1619 |
"tiktok_generic_fail": "نتوانستم این ویدیو یا اسلایدشو را از TikTok دانلود کنم. لینک دیگری امتحان کن یا کمی بعد تکرار کن.",
|
| 1620 |
"tiktok_author": "نویسنده TikTok",
|
| 1621 |
"tiktok_music": "موسیقی از TikTok",
|
|
|
|
| 1622 |
"status_generating_image": "در حال تولید تصویر",
|
| 1623 |
"status_voicing": "در حال تبدیل متن به گفتار",
|
| 1624 |
"status_taking_longer": "این کمی بیشتر طول میکشد…",
|
|
@@ -1699,6 +1719,7 @@ PACK_UR: dict[str, str] = {
|
|
| 1699 |
"tiktok_generic_fail": "یہ ویڈیو یا سلائیڈشو TikTok سے ڈاؤن لوڈ نہ ہو سکا۔ دوسرا لنک آزماؤ یا تھوڑی دیر بعد دہراؤ۔",
|
| 1700 |
"tiktok_author": "TikTok مصنف",
|
| 1701 |
"tiktok_music": "TikTok موسیقی",
|
|
|
|
| 1702 |
"status_generating_image": "تصویر بنائی جا رہی ہے",
|
| 1703 |
"status_voicing": "متن کو آواز دی جا رہی ہے",
|
| 1704 |
"status_taking_longer": "اس میں تھوڑا اور وقت لگے گا…",
|
|
@@ -1779,6 +1800,7 @@ PACK_UZ: dict[str, str] = {
|
|
| 1779 |
"tiktok_generic_fail": "Bu video yoki slaydshouni TikTok'dan yuklab bo'lmadi. Boshqa havola urin yoki birozdan keyin takrorla.",
|
| 1780 |
"tiktok_author": "TikTok muallifi",
|
| 1781 |
"tiktok_music": "TikTok musiqasi",
|
|
|
|
| 1782 |
"status_generating_image": "Rasm yaratilmoqda",
|
| 1783 |
"status_voicing": "Matn ovozlanmoqda",
|
| 1784 |
"status_taking_longer": "Bu biroz ko'proq vaqt oladi…",
|
|
@@ -1859,6 +1881,7 @@ PACK_BN: dict[str, str] = {
|
|
| 1859 |
"tiktok_generic_fail": "TikTok থেকে এই ভিডিও বা স্লাইডশো ডাউনলোড করা যায়নি। অন্য লিংক চেষ্টা করো বা একটু পরে পুনরাবৃত্তি করো।",
|
| 1860 |
"tiktok_author": "TikTok লেখক",
|
| 1861 |
"tiktok_music": "TikTok সঙ্গীত",
|
|
|
|
| 1862 |
"status_generating_image": "ছবি তৈরি হচ্ছে",
|
| 1863 |
"status_voicing": "টেক্সটে কণ্ঠ দেওয়া হচ্ছে",
|
| 1864 |
"status_taking_longer": "এতে আরও একটু সময় লাগবে…",
|
|
@@ -1939,6 +1962,7 @@ PACK_FIL: dict[str, str] = {
|
|
| 1939 |
"tiktok_generic_fail": "Hindi mada-download ang video o slideshow na ito mula sa TikTok. Subukan ang ibang link o ulitin mamaya.",
|
| 1940 |
"tiktok_author": "May-akda ng TikTok",
|
| 1941 |
"tiktok_music": "Musika mula sa TikTok",
|
|
|
|
| 1942 |
"status_generating_image": "Gumuguhit ng larawan",
|
| 1943 |
"status_voicing": "Binabasa ang text",
|
| 1944 |
"status_taking_longer": "Medyo matatagalan ito…",
|
|
@@ -2019,6 +2043,7 @@ PACK_PL: dict[str, str] = {
|
|
| 2019 |
"tiktok_generic_fail": "Nie udało się pobrać tego filmu ani pokazu z TikToka. Spróbuj inny link albo powtórz nieco później.",
|
| 2020 |
"tiktok_author": "Autor TikToka",
|
| 2021 |
"tiktok_music": "Muzyka z TikToka",
|
|
|
|
| 2022 |
"status_generating_image": "Generuję obraz",
|
| 2023 |
"status_voicing": "Odczytuję tekst",
|
| 2024 |
"status_taking_longer": "To zajmie trochę więcej czasu…",
|
|
@@ -2099,6 +2124,7 @@ PACK_NL: dict[str, str] = {
|
|
| 2099 |
"tiktok_generic_fail": "Deze video of slideshow kon niet van TikTok worden gedownload. Probeer een andere link of herhaal later.",
|
| 2100 |
"tiktok_author": "TikTok-auteur",
|
| 2101 |
"tiktok_music": "Muziek van TikTok",
|
|
|
|
| 2102 |
"status_generating_image": "Afbeelding genereren",
|
| 2103 |
"status_voicing": "Tekst uitspreken",
|
| 2104 |
"status_taking_longer": "Dit duurt iets langer…",
|
|
@@ -2179,6 +2205,7 @@ PACK_ZU: dict[str, str] = {
|
|
| 2179 |
"tiktok_generic_fail": "Ayikwazanga ukulandwa le vidiyo noma i-slideshow ku-TikTok. Zama esinye isixhumanisi noma phinda maduzane.",
|
| 2180 |
"tiktok_author": "Umbhali we-TikTok",
|
| 2181 |
"tiktok_music": "Umculo ovela ku-TikTok",
|
|
|
|
| 2182 |
"status_generating_image": "Kudweba isithombe",
|
| 2183 |
"status_voicing": "Kufundwa umbiko",
|
| 2184 |
"status_taking_longer": "Lokhu kuzothatha isikhathi eside…",
|
|
|
|
| 591 |
"ru": "Музыка из TikTok",
|
| 592 |
"uk": "Музика з TikTok",
|
| 593 |
},
|
| 594 |
+
"tiktok_dl_slideshow": {
|
| 595 |
+
"be": "Слайдшоў TikTok: {shown}/{total}",
|
| 596 |
+
"en": "TikTok slideshow: {shown}/{total}",
|
| 597 |
+
"es": "Presentación de TikTok: {shown}/{total}",
|
| 598 |
+
"kk": "TikTok слайдшоуы: {shown}/{total}",
|
| 599 |
+
"ru": "Слайдшоу TikTok: {shown}/{total}",
|
| 600 |
+
"uk": "Слайдшоу TikTok: {shown}/{total}",
|
| 601 |
+
},
|
| 602 |
# ── Статусы ──
|
| 603 |
"status_generating_image": {
|
| 604 |
"be": "Генерую малюнак",
|
|
|
|
| 747 |
"tiktok_generic_fail": "Não consegui baixar este vídeo ou apresentação do TikTok. Tente outro link ou repita um pouco mais tarde.",
|
| 748 |
"tiktok_author": "Autor do TikTok",
|
| 749 |
"tiktok_music": "Música do TikTok",
|
| 750 |
+
"tiktok_dl_slideshow": "Apresentação de slides do TikTok: {shown}/{total}",
|
| 751 |
"status_generating_image": "Gerando imagem",
|
| 752 |
"status_voicing": "Convertendo texto em voz",
|
| 753 |
"status_taking_longer": "Isso vai demorar um pouco mais…",
|
|
|
|
| 828 |
"tiktok_generic_fail": "تعذر تنزيل هذا الفيديو أو عرض الشرائح من TikTok. جرب رابطًا آخر أو أعد المحاولة بعد قليل.",
|
| 829 |
"tiktok_author": "مؤلف TikTok",
|
| 830 |
"tiktok_music": "موسيقى من TikTok",
|
| 831 |
+
"tiktok_dl_slideshow": "عرض شرائح TikTok: {shown}/{total}",
|
| 832 |
"status_generating_image": "جارٍ توليد الصورة",
|
| 833 |
"status_voicing": "جارٍ تحويل النص إلى صوت",
|
| 834 |
"status_taking_longer": "سيستغرق هذا وقتًا أطول قليلًا…",
|
|
|
|
| 909 |
"tiktok_generic_fail": "Bu video veya slayt TikTok'tan indirilemedi. Başka bağlantı dene veya biraz sonra tekrarla.",
|
| 910 |
"tiktok_author": "TikTok yazarı",
|
| 911 |
"tiktok_music": "TikTok müziği",
|
| 912 |
+
"tiktok_dl_slideshow": "TikTok slayt gösterisi: {shown}/{total}",
|
| 913 |
"status_generating_image": "Görsel oluşturuluyor",
|
| 914 |
"status_voicing": "Metin seslendiriliyor",
|
| 915 |
"status_taking_longer": "Bu biraz daha uzun sürecek…",
|
|
|
|
| 990 |
"tiktok_generic_fail": "Dieses Video bzw. diese Slideshow ließ sich nicht von TikTok laden. Anderen Link versuchen oder etwas später wiederholen.",
|
| 991 |
"tiktok_author": "TikTok-Autor",
|
| 992 |
"tiktok_music": "Musik von TikTok",
|
| 993 |
+
"tiktok_dl_slideshow": "TikTok-Diashow: {shown}/{total}",
|
| 994 |
"status_generating_image": "Bild wird erzeugt",
|
| 995 |
"status_voicing": "Text wird vertont",
|
| 996 |
"status_taking_longer": "Das dauert etwas länger…",
|
|
|
|
| 1071 |
"tiktok_generic_fail": "Impossible de télécharger cette vidéo ou ce diaporama depuis TikTok. Essaie un autre lien ou réessaie un peu plus tard.",
|
| 1072 |
"tiktok_author": "Auteur TikTok",
|
| 1073 |
"tiktok_music": "Musique de TikTok",
|
| 1074 |
+
"tiktok_dl_slideshow": "Diaporama TikTok : {shown}/{total}",
|
| 1075 |
"status_generating_image": "Génération de l'image",
|
| 1076 |
"status_voicing": "Conversion du texte en voix",
|
| 1077 |
"status_taking_longer": "Ça va prendre un peu plus de temps…",
|
|
|
|
| 1152 |
"tiktok_generic_fail": "Impossibile scaricare questo video o slideshow da TikTok. Prova un altro link o ripeti tra poco.",
|
| 1153 |
"tiktok_author": "Autore TikTok",
|
| 1154 |
"tiktok_music": "Musica di TikTok",
|
| 1155 |
+
"tiktok_dl_slideshow": "Presentazione TikTok: {shown}/{total}",
|
| 1156 |
"status_generating_image": "Generazione dell'immagine",
|
| 1157 |
"status_voicing": "Conversione del testo in voce",
|
| 1158 |
"status_taking_longer": "Ci vorrà un po' più di tempo…",
|
|
|
|
| 1233 |
"tiktok_generic_fail": "यह वीडियो या स्लाइडशो TikTok से डाउनलोड नहीं हो सका। दूसरा लिंक आज़माओ या थोड़ी देर बाद दोहराओ।",
|
| 1234 |
"tiktok_author": "TikTok लेखक",
|
| 1235 |
"tiktok_music": "TikTok संगीत",
|
| 1236 |
+
"tiktok_dl_slideshow": "TikTok स्लाइडशो: {shown}/{total}",
|
| 1237 |
"status_generating_image": "चित्र बनाया जा रहा है",
|
| 1238 |
"status_voicing": "टेक्स्ट को आवाज़ दी जा रही है",
|
| 1239 |
"status_taking_longer": "इसमें थोड़ा और समय लगेगा…",
|
|
|
|
| 1314 |
"tiktok_generic_fail": "Tidak bisa mengunduh video atau slideshow ini dari TikTok. Coba tautan lain atau ulangi sedikit lagi.",
|
| 1315 |
"tiktok_author": "Penulis TikTok",
|
| 1316 |
"tiktok_music": "Musik dari TikTok",
|
| 1317 |
+
"tiktok_dl_slideshow": "Tayangan slide TikTok: {shown}/{total}",
|
| 1318 |
"status_generating_image": "Membuat gambar",
|
| 1319 |
"status_voicing": "Mengucapkan teks",
|
| 1320 |
"status_taking_longer": "Ini akan memakan waktu sedikit lebih lama…",
|
|
|
|
| 1395 |
"tiktok_generic_fail": "Tidak dapat memuat turun video atau slaid ini dari TikTok. Cuba pautan lain atau ulangi sedikit lagi.",
|
| 1396 |
"tiktok_author": "Pengarang TikTok",
|
| 1397 |
"tiktok_music": "Muzik dari TikTok",
|
| 1398 |
+
"tiktok_dl_slideshow": "Tayangan slaid TikTok: {shown}/{total}",
|
| 1399 |
"status_generating_image": "Menjana gambar",
|
| 1400 |
"status_voicing": "Mengucapkan teks",
|
| 1401 |
"status_taking_longer": "Ini akan mengambil masa sedikit lebih lama…",
|
|
|
|
| 1476 |
"tiktok_generic_fail": "Không tải được video hay slideshow này từ TikTok. Thử liên kết khác hoặc lặp lại sau một lúc.",
|
| 1477 |
"tiktok_author": "Tác giả TikTok",
|
| 1478 |
"tiktok_music": "Nhạc từ TikTok",
|
| 1479 |
+
"tiktok_dl_slideshow": "Trình chiếu TikTok: {shown}/{total}",
|
| 1480 |
"status_generating_image": "Đang tạo ảnh",
|
| 1481 |
"status_voicing": "Đang đọc văn bản",
|
| 1482 |
"status_taking_longer": "Việc này sẽ mất lâu hơn một chút…",
|
|
|
|
| 1557 |
"tiktok_generic_fail": "ดาวน์โหลดวิดีโอหรือสไลด์โชว์นี้จาก TikTok ไม่ได้ ลองลิงก์อื่นหรือทำซ้ำอีกสักครู่",
|
| 1558 |
"tiktok_author": "ผู้เขียน TikTok",
|
| 1559 |
"tiktok_music": "เพลงจาก TikTok",
|
| 1560 |
+
"tiktok_dl_slideshow": "สไลด์โชว์ TikTok: {shown}/{total}",
|
| 1561 |
"status_generating_image": "กำลังสร้างรูปภาพ",
|
| 1562 |
"status_voicing": "กำลังอ่านข้อความ",
|
| 1563 |
"status_taking_longer": "อันนี้จะใช้เวลานานกว่านิดหน่อย…",
|
|
|
|
| 1638 |
"tiktok_generic_fail": "نتوانستم این ویدیو یا اسلایدشو را از TikTok دانلود کنم. لینک دیگری امتحان کن یا کمی بعد تکرار کن.",
|
| 1639 |
"tiktok_author": "نویسنده TikTok",
|
| 1640 |
"tiktok_music": "موسیقی از TikTok",
|
| 1641 |
+
"tiktok_dl_slideshow": "اسلایدشوی TikTok: {shown}/{total}",
|
| 1642 |
"status_generating_image": "در حال تولید تصویر",
|
| 1643 |
"status_voicing": "در حال تبدیل متن به گفتار",
|
| 1644 |
"status_taking_longer": "این کمی بیشتر طول میکشد…",
|
|
|
|
| 1719 |
"tiktok_generic_fail": "یہ ویڈیو یا سلائیڈشو TikTok سے ڈاؤن لوڈ نہ ہو سکا۔ دوسرا لنک آزماؤ یا تھوڑی دیر بعد دہراؤ۔",
|
| 1720 |
"tiktok_author": "TikTok مصنف",
|
| 1721 |
"tiktok_music": "TikTok موسیقی",
|
| 1722 |
+
"tiktok_dl_slideshow": "TikTok سلائیڈ شو: {shown}/{total}",
|
| 1723 |
"status_generating_image": "تصویر بنائی جا رہی ہے",
|
| 1724 |
"status_voicing": "متن کو آواز دی جا رہی ہے",
|
| 1725 |
"status_taking_longer": "اس میں تھوڑا اور وقت لگے گا…",
|
|
|
|
| 1800 |
"tiktok_generic_fail": "Bu video yoki slaydshouni TikTok'dan yuklab bo'lmadi. Boshqa havola urin yoki birozdan keyin takrorla.",
|
| 1801 |
"tiktok_author": "TikTok muallifi",
|
| 1802 |
"tiktok_music": "TikTok musiqasi",
|
| 1803 |
+
"tiktok_dl_slideshow": "TikTok slayd-shousi: {shown}/{total}",
|
| 1804 |
"status_generating_image": "Rasm yaratilmoqda",
|
| 1805 |
"status_voicing": "Matn ovozlanmoqda",
|
| 1806 |
"status_taking_longer": "Bu biroz ko'proq vaqt oladi…",
|
|
|
|
| 1881 |
"tiktok_generic_fail": "TikTok থেকে এই ভিডিও বা স্লাইডশো ডাউনলোড করা যায়নি। অন্য লিংক চেষ্টা করো বা একটু পরে পুনরাবৃত্তি করো।",
|
| 1882 |
"tiktok_author": "TikTok লেখক",
|
| 1883 |
"tiktok_music": "TikTok সঙ্গীত",
|
| 1884 |
+
"tiktok_dl_slideshow": "TikTok স্লাইডশো: {shown}/{total}",
|
| 1885 |
"status_generating_image": "ছবি তৈরি হচ্ছে",
|
| 1886 |
"status_voicing": "টেক্সটে কণ্ঠ দেওয়া হচ্ছে",
|
| 1887 |
"status_taking_longer": "এতে আরও একটু সময় লাগবে…",
|
|
|
|
| 1962 |
"tiktok_generic_fail": "Hindi mada-download ang video o slideshow na ito mula sa TikTok. Subukan ang ibang link o ulitin mamaya.",
|
| 1963 |
"tiktok_author": "May-akda ng TikTok",
|
| 1964 |
"tiktok_music": "Musika mula sa TikTok",
|
| 1965 |
+
"tiktok_dl_slideshow": "Slideshow ng TikTok: {shown}/{total}",
|
| 1966 |
"status_generating_image": "Gumuguhit ng larawan",
|
| 1967 |
"status_voicing": "Binabasa ang text",
|
| 1968 |
"status_taking_longer": "Medyo matatagalan ito…",
|
|
|
|
| 2043 |
"tiktok_generic_fail": "Nie udało się pobrać tego filmu ani pokazu z TikToka. Spróbuj inny link albo powtórz nieco później.",
|
| 2044 |
"tiktok_author": "Autor TikToka",
|
| 2045 |
"tiktok_music": "Muzyka z TikToka",
|
| 2046 |
+
"tiktok_dl_slideshow": "Pokaz slajdów TikTok: {shown}/{total}",
|
| 2047 |
"status_generating_image": "Generuję obraz",
|
| 2048 |
"status_voicing": "Odczytuję tekst",
|
| 2049 |
"status_taking_longer": "To zajmie trochę więcej czasu…",
|
|
|
|
| 2124 |
"tiktok_generic_fail": "Deze video of slideshow kon niet van TikTok worden gedownload. Probeer een andere link of herhaal later.",
|
| 2125 |
"tiktok_author": "TikTok-auteur",
|
| 2126 |
"tiktok_music": "Muziek van TikTok",
|
| 2127 |
+
"tiktok_dl_slideshow": "Diavoorstelling TikTok: {shown}/{total}",
|
| 2128 |
"status_generating_image": "Afbeelding genereren",
|
| 2129 |
"status_voicing": "Tekst uitspreken",
|
| 2130 |
"status_taking_longer": "Dit duurt iets langer…",
|
|
|
|
| 2205 |
"tiktok_generic_fail": "Ayikwazanga ukulandwa le vidiyo noma i-slideshow ku-TikTok. Zama esinye isixhumanisi noma phinda maduzane.",
|
| 2206 |
"tiktok_author": "Umbhali we-TikTok",
|
| 2207 |
"tiktok_music": "Umculo ovela ku-TikTok",
|
| 2208 |
+
"tiktok_dl_slideshow": "Isilayidi se-TikTok: {shown}/{total}",
|
| 2209 |
"status_generating_image": "Kudweba isithombe",
|
| 2210 |
"status_voicing": "Kufundwa umbiko",
|
| 2211 |
"status_taking_longer": "Lokhu kuzothatha isikhathi eside…",
|
lumen_tiktok.py
CHANGED
|
@@ -315,6 +315,26 @@ async def _communicate_process(proc: asyncio.subprocess.Process, *, timeout: flo
|
|
| 315 |
raise
|
| 316 |
|
| 317 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
async def _probe_video_dimensions(path: str) -> tuple[int, int, int]:
|
| 319 |
"""Возвращает (duration, width, height): без них Telegram показывает видео "сырым файлом" 0:00 (TikTok не всегда ставит faststart)."""
|
| 320 |
try:
|
|
|
|
| 315 |
raise
|
| 316 |
|
| 317 |
|
| 318 |
+
async def _probe_audio_duration(path: str) -> int:
|
| 319 |
+
"""Длительность аудиофайла в секундах (для send_audio — без неё Telegram показывает 0:00). 0 при любой неудаче."""
|
| 320 |
+
try:
|
| 321 |
+
proc = await asyncio.create_subprocess_exec(
|
| 322 |
+
"ffprobe", "-v", "error",
|
| 323 |
+
"-show_entries", "format=duration",
|
| 324 |
+
"-of", "default=noprint_wrappers=1:nokey=1",
|
| 325 |
+
path,
|
| 326 |
+
stdout=asyncio.subprocess.PIPE,
|
| 327 |
+
stderr=asyncio.subprocess.PIPE,
|
| 328 |
+
)
|
| 329 |
+
stdout, _ = await _communicate_process(proc, timeout=15)
|
| 330 |
+
raw = stdout.decode(errors="replace").strip().splitlines()
|
| 331 |
+
if raw and raw[0] and raw[0] != "N/A":
|
| 332 |
+
return max(1, round(float(raw[0])))
|
| 333 |
+
except Exception as e:
|
| 334 |
+
log.warning("[ffmpeg] Audio probe failed: %s", e)
|
| 335 |
+
return 0
|
| 336 |
+
|
| 337 |
+
|
| 338 |
async def _probe_video_dimensions(path: str) -> tuple[int, int, int]:
|
| 339 |
"""Возвращает (duration, width, height): без них Telegram показывает видео "сырым файлом" 0:00 (TikTok не всегда ставит faststart)."""
|
| 340 |
try:
|
lumen_tiktok_flow.py
CHANGED
|
@@ -101,6 +101,7 @@ async def _send_tiktok_music(session, media_data: dict, message: Message, author
|
|
| 101 |
|
| 102 |
# вшиваем метаданные и обложку в MP3 перед отправкой — чтобы теги видели и другие плееры
|
| 103 |
tagged_music_bytes = music_bytes
|
|
|
|
| 104 |
try:
|
| 105 |
with tempfile.TemporaryDirectory() as tmp_dir:
|
| 106 |
tmp_mp3_path = os.path.join(tmp_dir, "music.mp3")
|
|
@@ -110,6 +111,8 @@ async def _send_tiktok_music(session, media_data: dict, message: Message, author
|
|
| 110 |
if os.path.exists(tmp_mp3_path) and os.path.getsize(tmp_mp3_path) > 0:
|
| 111 |
with open(tmp_mp3_path, "rb") as f:
|
| 112 |
tagged_music_bytes = f.read()
|
|
|
|
|
|
|
| 113 |
except Exception as tag_err:
|
| 114 |
log.warning("[tiktok] failed to write embedded tags to MP3: %s", tag_err)
|
| 115 |
|
|
@@ -117,10 +120,11 @@ async def _send_tiktok_music(session, media_data: dict, message: Message, author
|
|
| 117 |
# иначе multipart-имя файла битое.
|
| 118 |
safe_title = re.sub(r'[\\/:*?"<>|\x00-\x1f]', "_", cleaned_title[:60]).strip() or "track"
|
| 119 |
await bot.bot.send_audio(
|
| 120 |
-
|
| 121 |
-
|
| 122 |
title=cleaned_title,
|
| 123 |
performer=performer_name,
|
|
|
|
| 124 |
thumbnail=thumbnail_file,
|
| 125 |
reply_to_message_id=message.message_id
|
| 126 |
)
|
|
@@ -166,10 +170,11 @@ async def _try_send_tiktok_slideshow(
|
|
| 166 |
if images and isinstance(images, list):
|
| 167 |
# TikTok разрешает до 35 слайдов, Telegram — 10 за вызов: берём весь пост и шлём несколькими группами.
|
| 168 |
images_to_fetch = images[:bot.TIKTOK_SLIDESHOW_MAX_ITEMS]
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
|
|
|
| 173 |
# Качаем слайды параллельно gather; конкурентность режем семафором, чтобы не занимать весь пул сессии.
|
| 174 |
fetch_urls = _slideshow_slide_urls(media_data, images_to_fetch)
|
| 175 |
|
|
@@ -217,7 +222,7 @@ async def _try_send_tiktok_slideshow(
|
|
| 217 |
else:
|
| 218 |
media_items.append(InputMediaPhoto(media=BufferedInputFile(item_bytes, filename=f"photo_{idx}.jpg")))
|
| 219 |
if media_items:
|
| 220 |
-
|
| 221 |
if len(media_items) == 1:
|
| 222 |
# Один уцелевший слайд — обычным send_photo/send_video: media group требует минимум 2.
|
| 223 |
only_item = media_items[0]
|
|
@@ -243,6 +248,7 @@ async def _try_send_tiktok_slideshow(
|
|
| 243 |
# Пауза между группами — против анти-флуда.
|
| 244 |
await asyncio.sleep(0.3)
|
| 245 |
await _send_tiktok_music(session, media_data, message, author, headers)
|
|
|
|
| 246 |
return True
|
| 247 |
return False
|
| 248 |
|
|
@@ -315,8 +321,8 @@ async def _send_tiktok_single_video(
|
|
| 315 |
)
|
| 316 |
continue
|
| 317 |
|
| 318 |
-
await bot._delete_message_quietly(status)
|
| 319 |
await _send_tiktok_music(session, media_data, message, author, headers)
|
|
|
|
| 320 |
return
|
| 321 |
|
| 322 |
if hit_size_limit:
|
|
|
|
| 101 |
|
| 102 |
# вшиваем метаданные и обложку в MP3 перед отправкой — чтобы теги видели и другие плееры
|
| 103 |
tagged_music_bytes = music_bytes
|
| 104 |
+
music_duration = 0
|
| 105 |
try:
|
| 106 |
with tempfile.TemporaryDirectory() as tmp_dir:
|
| 107 |
tmp_mp3_path = os.path.join(tmp_dir, "music.mp3")
|
|
|
|
| 111 |
if os.path.exists(tmp_mp3_path) and os.path.getsize(tmp_mp3_path) > 0:
|
| 112 |
with open(tmp_mp3_path, "rb") as f:
|
| 113 |
tagged_music_bytes = f.read()
|
| 114 |
+
# Длительность — явно: без неё Telegram показывает 0:00 (та же история, что была с видео).
|
| 115 |
+
music_duration = await bot._probe_audio_duration(tmp_mp3_path)
|
| 116 |
except Exception as tag_err:
|
| 117 |
log.warning("[tiktok] failed to write embedded tags to MP3: %s", tag_err)
|
| 118 |
|
|
|
|
| 120 |
# иначе multipart-имя файла битое.
|
| 121 |
safe_title = re.sub(r'[\\/:*?"<>|\x00-\x1f]', "_", cleaned_title[:60]).strip() or "track"
|
| 122 |
await bot.bot.send_audio(
|
| 123 |
+
chat_id=message.chat.id,
|
| 124 |
+
audio=BufferedInputFile(tagged_music_bytes, filename=f"{safe_title}.mp3"),
|
| 125 |
title=cleaned_title,
|
| 126 |
performer=performer_name,
|
| 127 |
+
duration=music_duration if music_duration > 0 else None,
|
| 128 |
thumbnail=thumbnail_file,
|
| 129 |
reply_to_message_id=message.message_id
|
| 130 |
)
|
|
|
|
| 170 |
if images and isinstance(images, list):
|
| 171 |
# TikTok разрешает до 35 слайдов, Telegram — 10 за вызов: берём весь пост и шлём несколькими группами.
|
| 172 |
images_to_fetch = images[:bot.TIKTOK_SLIDESHOW_MAX_ITEMS]
|
| 173 |
+
# Статус слайдшоу — через переводы (был захардкожен по-русски).
|
| 174 |
+
await bot._edit_message_quietly(status, bot._t(
|
| 175 |
+
message.chat.id, "tiktok_dl_slideshow",
|
| 176 |
+
shown=len(images_to_fetch), total=len(images),
|
| 177 |
+
))
|
| 178 |
# Качаем слайды параллельно gather; конкурентность режем семафором, чтобы не занимать весь пул сессии.
|
| 179 |
fetch_urls = _slideshow_slide_urls(media_data, images_to_fetch)
|
| 180 |
|
|
|
|
| 222 |
else:
|
| 223 |
media_items.append(InputMediaPhoto(media=BufferedInputFile(item_bytes, filename=f"photo_{idx}.jpg")))
|
| 224 |
if media_items:
|
| 225 |
+
# Статус держим до конца (включая музыку ниже) — иначе человек висит в тишине, пока качается трек.
|
| 226 |
if len(media_items) == 1:
|
| 227 |
# Один уцелевший слайд — обычным send_photo/send_video: media group требует минимум 2.
|
| 228 |
only_item = media_items[0]
|
|
|
|
| 248 |
# Пауза между группами — против анти-флуда.
|
| 249 |
await asyncio.sleep(0.3)
|
| 250 |
await _send_tiktok_music(session, media_data, message, author, headers)
|
| 251 |
+
await bot._delete_message_quietly(status)
|
| 252 |
return True
|
| 253 |
return False
|
| 254 |
|
|
|
|
| 321 |
)
|
| 322 |
continue
|
| 323 |
|
|
|
|
| 324 |
await _send_tiktok_music(session, media_data, message, author, headers)
|
| 325 |
+
await bot._delete_message_quietly(status)
|
| 326 |
return
|
| 327 |
|
| 328 |
if hit_size_limit:
|
tests/test_bot_tiktok.py
CHANGED
|
@@ -1087,3 +1087,131 @@ def test_communicate_process_propagates_communicate_failure():
|
|
| 1087 |
|
| 1088 |
asyncio.run(run())
|
| 1089 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1087 |
|
| 1088 |
asyncio.run(run())
|
| 1089 |
|
| 1090 |
+
|
| 1091 |
+
def test_slideshow_status_uses_localized_key():
|
| 1092 |
+
# Статус слайдшоу был захардкожен по-русски — теперь ключ tiktok_dl_slideshow с плейсхолдерами.
|
| 1093 |
+
text = bot._t(999451, "tiktok_dl_slideshow", shown=35, total=40)
|
| 1094 |
+
assert "35/40" in text
|
| 1095 |
+
try:
|
| 1096 |
+
assert "слайдов" in text or "slideshow" in text.lower() or "Слайдшоу" in text
|
| 1097 |
+
finally:
|
| 1098 |
+
bot.chat_state.pop(999451, None)
|
| 1099 |
+
|
| 1100 |
+
|
| 1101 |
+
def test_send_tiktok_music_passes_audio_duration():
|
| 1102 |
+
# Без явной длительности Telegram показывает 0:00 — пробинг обязан доезжать до send_audio.
|
| 1103 |
+
import lumen_tiktok_flow
|
| 1104 |
+
|
| 1105 |
+
incoming = _FakeIncomingMessage(999452)
|
| 1106 |
+
incoming.message_id = 1
|
| 1107 |
+
incoming.from_user = SimpleNamespace(language_code="ru")
|
| 1108 |
+
sent = {}
|
| 1109 |
+
|
| 1110 |
+
class _AudioBot:
|
| 1111 |
+
async def send_audio(self, **kwargs):
|
| 1112 |
+
sent.update(kwargs)
|
| 1113 |
+
return SimpleNamespace()
|
| 1114 |
+
|
| 1115 |
+
async def fake_download(session, url, headers=None):
|
| 1116 |
+
return b"fake-bytes"
|
| 1117 |
+
|
| 1118 |
+
async def fake_probe(path):
|
| 1119 |
+
return 42
|
| 1120 |
+
|
| 1121 |
+
def fake_write_tags(path, title, artist, cover):
|
| 1122 |
+
pass
|
| 1123 |
+
|
| 1124 |
+
media_data = {
|
| 1125 |
+
"music": "https://tikwm.com/song.mp3",
|
| 1126 |
+
"music_info": {"title": "Song", "author": "Auth"},
|
| 1127 |
+
"author": {"nickname": "Nick", "unique_id": "@nick"},
|
| 1128 |
+
}
|
| 1129 |
+
original_download = bot._download_url_bin
|
| 1130 |
+
original_write_tags = bot._write_mp3_tags
|
| 1131 |
+
original_probe = bot._probe_audio_duration
|
| 1132 |
+
original_bot = bot.bot
|
| 1133 |
+
bot._download_url_bin = fake_download
|
| 1134 |
+
bot._write_mp3_tags = fake_write_tags
|
| 1135 |
+
bot._probe_audio_duration = fake_probe
|
| 1136 |
+
bot.bot = _AudioBot()
|
| 1137 |
+
try:
|
| 1138 |
+
asyncio.run(lumen_tiktok_flow._send_tiktok_music(None, media_data, incoming, "Nick", {}))
|
| 1139 |
+
assert sent.get("duration") == 42
|
| 1140 |
+
assert sent.get("title") == "Song"
|
| 1141 |
+
finally:
|
| 1142 |
+
bot._download_url_bin = original_download
|
| 1143 |
+
bot._write_mp3_tags = original_write_tags
|
| 1144 |
+
bot._probe_audio_duration = original_probe
|
| 1145 |
+
bot.bot = original_bot
|
| 1146 |
+
bot.chat_state.pop(999452, None)
|
| 1147 |
+
|
| 1148 |
+
|
| 1149 |
+
def test_single_video_status_deleted_after_music():
|
| 1150 |
+
# Статус живёт и во время скачивания музыки тоже — сносится только после неё, а не до.
|
| 1151 |
+
import lumen_tiktok_flow
|
| 1152 |
+
|
| 1153 |
+
tikwm_json = {
|
| 1154 |
+
"code": 0,
|
| 1155 |
+
"data": {
|
| 1156 |
+
"play": "https://tikwm.com/sd.mp4", "size": 1000,
|
| 1157 |
+
"author": {"nickname": "TestAuthor"},
|
| 1158 |
+
},
|
| 1159 |
+
}
|
| 1160 |
+
incoming = _FakeIncomingMessage(999453)
|
| 1161 |
+
incoming.message_id = 1
|
| 1162 |
+
fake_bot = _FakeTikTokBot()
|
| 1163 |
+
events = []
|
| 1164 |
+
|
| 1165 |
+
async def fake_get_http_session():
|
| 1166 |
+
return _FakeTikTokSession(tikwm_json)
|
| 1167 |
+
|
| 1168 |
+
async def fake_resolve(session, url):
|
| 1169 |
+
return url
|
| 1170 |
+
|
| 1171 |
+
async def fake_download_url_bin(session, url, headers=None):
|
| 1172 |
+
return b"\x00" * 100
|
| 1173 |
+
|
| 1174 |
+
async def fake_probe_dims(path):
|
| 1175 |
+
return 0, 0, 0
|
| 1176 |
+
|
| 1177 |
+
async def fake_thumb(path, duration):
|
| 1178 |
+
return None
|
| 1179 |
+
|
| 1180 |
+
async def rec_music(*args, **kwargs):
|
| 1181 |
+
events.append("music")
|
| 1182 |
+
return None
|
| 1183 |
+
|
| 1184 |
+
async def rec_delete(message):
|
| 1185 |
+
events.append("delete")
|
| 1186 |
+
return True
|
| 1187 |
+
|
| 1188 |
+
original_get_session = bot._get_http_session
|
| 1189 |
+
original_resolve = bot._resolve_tiktok_short
|
| 1190 |
+
original_download = bot._download_url_bin
|
| 1191 |
+
original_probe = bot._probe_video_dimensions
|
| 1192 |
+
original_thumb = bot._generate_video_thumbnail
|
| 1193 |
+
original_bot = bot.bot
|
| 1194 |
+
original_delete = bot._delete_message_quietly
|
| 1195 |
+
original_music = lumen_tiktok_flow._send_tiktok_music
|
| 1196 |
+
bot._get_http_session = fake_get_http_session
|
| 1197 |
+
bot._resolve_tiktok_short = fake_resolve
|
| 1198 |
+
bot._download_url_bin = fake_download_url_bin
|
| 1199 |
+
bot._probe_video_dimensions = fake_probe_dims
|
| 1200 |
+
bot._generate_video_thumbnail = fake_thumb
|
| 1201 |
+
bot.bot = fake_bot
|
| 1202 |
+
bot._delete_message_quietly = rec_delete
|
| 1203 |
+
lumen_tiktok_flow._send_tiktok_music = rec_music
|
| 1204 |
+
try:
|
| 1205 |
+
asyncio.run(bot.handle_tiktok(incoming, "https://www.tiktok.com/@test/video/123"))
|
| 1206 |
+
assert events == ["music", "delete"]
|
| 1207 |
+
assert len(fake_bot.sent_videos) == 1
|
| 1208 |
+
finally:
|
| 1209 |
+
bot._get_http_session = original_get_session
|
| 1210 |
+
bot._resolve_tiktok_short = original_resolve
|
| 1211 |
+
bot._download_url_bin = original_download
|
| 1212 |
+
bot._probe_video_dimensions = original_probe
|
| 1213 |
+
bot._generate_video_thumbnail = original_thumb
|
| 1214 |
+
bot.bot = original_bot
|
| 1215 |
+
bot._delete_message_quietly = original_delete
|
| 1216 |
+
lumen_tiktok_flow._send_tiktok_music = original_music
|
| 1217 |
+
|