Spaces:
Running
Running
Merge titleswap fix
Browse files- lumen_tiktok_flow.py +13 -0
- tests/test_bot_tiktok.py +56 -0
lumen_tiktok_flow.py
CHANGED
|
@@ -84,6 +84,19 @@ async def _send_tiktok_music(session, media_data: dict, message: Message, author
|
|
| 84 |
# Именованный трек или "оригинальный" с названием — реальное название важнее подписи; очищенный остаток — если был префикс.
|
| 85 |
cleaned_title = residual_title if (mentions_generic_phrase and residual_title) else raw_music_title
|
| 86 |
performer_name = raw_music_author
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
|
| 88 |
# достаём обложку трека
|
| 89 |
cover_url = music_info.get("cover") or music_info.get("avatar") or media_data.get("author", {}).get("avatar")
|
|
|
|
| 84 |
# Именованный трек или "оригинальный" с названием — реальное название важнее подписи; очищенный остаток — если был префикс.
|
| 85 |
cleaned_title = residual_title if (mentions_generic_phrase and residual_title) else raw_music_title
|
| 86 |
performer_name = raw_music_author
|
| 87 |
+
# TikWM иногда привозит поля перевёрнутыми (прод 22.09.2026: в title лежал юзернейм
|
| 88 |
+
# автора видео, в author — название трека; по той же ссылке в другой раз — наоборот).
|
| 89 |
+
# Чиним очевидный случай: название без единого пробела совпало с хендлом автора видео,
|
| 90 |
+
# а в "авторе" — фраза с пробелами. Однословный настоящий трек под правило не попадает:
|
| 91 |
+
# он не равен хендлу автора.
|
| 92 |
+
_title_handle = cleaned_title.strip().lower().lstrip("@")
|
| 93 |
+
_known_handles = {h for h in (author_uniq_clean.lower(), author_nick.lower()) if h}
|
| 94 |
+
if _title_handle in _known_handles and re.search(r"\s", performer_name or ""):
|
| 95 |
+
log.info(
|
| 96 |
+
"[tiktok-music][diag] Swapped music title/author from TikWM (title=%r looked like author handle, author=%r looked like a title) — unswapping.",
|
| 97 |
+
cleaned_title, performer_name,
|
| 98 |
+
)
|
| 99 |
+
cleaned_title, performer_name = performer_name, author_uniq_clean or author_nick or performer_name
|
| 100 |
|
| 101 |
# достаём обложку трека
|
| 102 |
cover_url = music_info.get("cover") or music_info.get("avatar") or media_data.get("author", {}).get("avatar")
|
tests/test_bot_tiktok.py
CHANGED
|
@@ -1146,6 +1146,62 @@ def test_send_tiktok_music_passes_audio_duration():
|
|
| 1146 |
bot.chat_state.pop(999452, None)
|
| 1147 |
|
| 1148 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1149 |
def test_single_video_status_deleted_after_music():
|
| 1150 |
# Статус живёт и во время скачивания музыки тоже — сносится только после неё, а не до.
|
| 1151 |
import lumen_tiktok_flow
|
|
|
|
| 1146 |
bot.chat_state.pop(999452, None)
|
| 1147 |
|
| 1148 |
|
| 1149 |
+
def _run_music_capturing_audio(media_data, chat_id=999454):
|
| 1150 |
+
import lumen_tiktok_flow
|
| 1151 |
+
incoming = _FakeIncomingMessage(chat_id)
|
| 1152 |
+
incoming.message_id = 1
|
| 1153 |
+
incoming.from_user = SimpleNamespace(language_code="ru")
|
| 1154 |
+
sent = {}
|
| 1155 |
+
|
| 1156 |
+
class _AudioBot:
|
| 1157 |
+
async def send_audio(self, **kwargs):
|
| 1158 |
+
sent.update(kwargs)
|
| 1159 |
+
return SimpleNamespace()
|
| 1160 |
+
|
| 1161 |
+
async def fake_download(session, url, headers=None):
|
| 1162 |
+
return b"fake-bytes"
|
| 1163 |
+
|
| 1164 |
+
def fake_write_tags(path, title, artist, cover):
|
| 1165 |
+
pass
|
| 1166 |
+
|
| 1167 |
+
original_download = bot._download_url_bin
|
| 1168 |
+
original_write_tags = bot._write_mp3_tags
|
| 1169 |
+
original_bot = bot.bot
|
| 1170 |
+
bot._download_url_bin = fake_download
|
| 1171 |
+
bot._write_mp3_tags = fake_write_tags
|
| 1172 |
+
bot.bot = _AudioBot()
|
| 1173 |
+
try:
|
| 1174 |
+
asyncio.run(lumen_tiktok_flow._send_tiktok_music(None, media_data, incoming, "Nick", {}))
|
| 1175 |
+
finally:
|
| 1176 |
+
bot._download_url_bin = original_download
|
| 1177 |
+
bot._write_mp3_tags = original_write_tags
|
| 1178 |
+
bot.bot = original_bot
|
| 1179 |
+
bot.chat_state.pop(chat_id, None)
|
| 1180 |
+
return sent
|
| 1181 |
+
|
| 1182 |
+
|
| 1183 |
+
def test_send_tiktok_music_unswaps_tikwm_title_author():
|
| 1184 |
+
# Прод 22.09.2026: TikWM иногда кладёт юзернейм в title, а название трека — в author.
|
| 1185 |
+
sent = _run_music_capturing_audio({
|
| 1186 |
+
"music": "https://tikwm.com/song.mp3",
|
| 1187 |
+
"music_info": {"title": "milka_kicm12", "author": "Миля, ты че творишь"},
|
| 1188 |
+
"author": {"nickname": "Milka", "unique_id": "milka_kicm12"},
|
| 1189 |
+
})
|
| 1190 |
+
assert sent.get("title") == "Миля, ты че творишь"
|
| 1191 |
+
assert sent.get("performer") == "milka_kicm12"
|
| 1192 |
+
|
| 1193 |
+
|
| 1194 |
+
def test_send_tiktok_music_keeps_one_word_track_title():
|
| 1195 |
+
# Однословный настоящий трек НЕ равен хендлу автора — не трогаем.
|
| 1196 |
+
sent = _run_music_capturing_audio({
|
| 1197 |
+
"music": "https://tikwm.com/song.mp3",
|
| 1198 |
+
"music_info": {"title": "Believer", "author": "Imagine Dragons"},
|
| 1199 |
+
"author": {"nickname": "Other", "unique_id": "other_user"},
|
| 1200 |
+
})
|
| 1201 |
+
assert sent.get("title") == "Believer"
|
| 1202 |
+
assert sent.get("performer") == "Imagine Dragons"
|
| 1203 |
+
|
| 1204 |
+
|
| 1205 |
def test_single_video_status_deleted_after_music():
|
| 1206 |
# Статус живёт и во время скачивания музыки тоже — сносится только после неё, а не до.
|
| 1207 |
import lumen_tiktok_flow
|