Spaces:
Running
Running
File size: 23,942 Bytes
529b973 b737d7a 529b973 6c3a31d b737d7a 6c3a31d 529b973 6c3a31d 529b973 6c3a31d 529b973 6c3a31d 529b973 6c3a31d 529b973 6c3a31d 529b973 6c3a31d 529b973 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 | """Streaming renames: a big file goes straight from the download into the upload, and anything that needs the file takes the usual way."""
import shutil
from pathlib import Path
from types import SimpleNamespace
import pytest
from telethon import errors, types
from bot import errorlog
from bot import fasttransfer as ft
from bot import pipeline
from bot.config import load_config
from bot.db import Database
from bot.errors import Cancelled
from bot.media import probe
from bot.pipeline import Renamer, embedded_thumb, make_caption, source_attributes
from bot.queue import Job
from bot.utils import human_duration, human_size
from .appkit import ensure_auto
from .conftest import needs_ffmpeg
MIB = 1024 * 1024
BIG = 40 * MIB # more than the 10 MB Telegram's big-file upload starts at; nothing this size is really moved: the engine is a stub here
NAME = "[SubsPlease] Tokyo Revengers - 05 (1080p) [ABCD1234].mkv"
NEW_NAME = "[S01E05] Tokyo Revengers [1080p].mkv"
PREVIEW = b"\xff\xd8preview-of-the-original"
def video_attr(duration=1400.5, w=1920, h=1080, **kw):
return types.DocumentAttributeVideo(duration=duration, w=w, h=h, supports_streaming=True, **kw)
def audio_attr(duration=215.4, title="Song", performer="Me", **kw):
return types.DocumentAttributeAudio(duration=duration, title=title, performer=performer, **kw)
def preview_sizes():
return [types.PhotoStrippedSize(type="i", bytes=b"\x01\x02"), types.PhotoSize(type="m", w=320, h=180, size=900)]
class FakeMessage:
"""A message with a document, the way Telethon shows it to the pipeline."""
def __init__(self, name=NAME, size=BIG, *, attributes=(), thumbs=None, mime="video/x-matroska", msg_id=7, document=True):
self.id = msg_id
self.file = SimpleNamespace(name=name, size=size, ext=Path(name or "").suffix, mime_type=mime)
self.document = SimpleNamespace(attributes=list(attributes), thumbs=thumbs, size=size, mime_type=mime) if document else None
video, audio = source_attributes(self)
self.video = self.document if video is not None else None # like Telethon's own properties
self.audio = self.document if audio is not None else None
class FakeClient:
def __init__(self, message, source):
self.message, self.source = message, source
self.sent, self.edits = [], []
self.relays, self.downloads, self.uploads, self.preview_downloads = [], [], [], 0
self.relay_error = None
self.send_errors = []
async def get_messages(self, chat_id, ids):
return self.message
async def download_media(self, message, file=None, thumb=None, progress_callback=None):
assert thumb is not None, "the whole file is fetched by transfer.download, which is stubbed"
self.preview_downloads += 1
return PREVIEW
async def send_file(self, target, input_file, **kwargs):
if self.send_errors:
raise self.send_errors.pop(0)
self.sent.append((target, input_file, kwargs))
async def edit_message(self, message, text, buttons=None, parse_mode=None):
self.edits.append((text, buttons))
@pytest.fixture(autouse=True)
def fresh_stats(monkeypatch):
monkeypatch.setattr(ft, "stats", ft.TransferStats())
@pytest.fixture
async def env(tmp_path, media_dir, monkeypatch):
def new_cfg(**environ):
return load_config({
"API_ID": "1", "API_HASH": "h", "BOT_TOKEN": "1:t", "OWNER_ID": "42", "TEMP_DIR": str(tmp_path / "work"),
"PROGRESS_INTERVAL": "2", **environ, # fast transfer and streaming are on unless a test turns them off
})
db = Database(f"sqlite+aiosqlite:///{tmp_path}/s.db")
await db.init()
ns = SimpleNamespace(db=db, media_dir=media_dir, tmp=tmp_path, new_cfg=new_cfg, cfg=new_cfg())
async def relay(client, document, file_name, *, connections, progress=None, cancel=None):
client.relays.append((document, file_name, connections))
if client.relay_error is not None:
raise client.relay_error
progress(document.size // 2, document.size)
progress(document.size, document.size)
return types.InputFileBig(id=99, parts=-(-document.size // ft.UPLOAD_PART), name=file_name)
async def download(client, message, dest, *, fast, connections, progress=None, cancel=None, max_bytes=None):
client.downloads.append(Path(dest).name)
shutil.copy(client.source, dest)
async def upload(client, path, file_name, *, fast, connections, progress=None, cancel=None):
client.uploads.append(Path(path).read_bytes())
return types.InputFileBig(id=1, parts=1, name=file_name)
monkeypatch.setattr(ft, "relay", relay)
monkeypatch.setattr(ft, "download", download)
monkeypatch.setattr(ft, "upload", upload)
yield ns
await db.close()
@pytest.fixture
async def log_channel():
"""What reaches the log channel while a test runs."""
reports = []
async def send(text):
reports.append(text)
handler = errorlog.install(send)
yield SimpleNamespace(reports=reports, flush=handler.aclose)
handler.detach()
async def rename(env, message=None, *, cfg=None, new_name=NEW_NAME, fmt=None, source="sample.mkv"):
"""Run one job; returns ``(client, job)``."""
message = message or FakeMessage(attributes=[video_attr()], thumbs=preview_sizes())
client = FakeClient(message, env.media_dir / source)
job = Job(id=1, user_id=42, chat_id=42, msg_id=7, file_name=message.file.name or "x", size=message.file.size, new_name=new_name,
status_msg=SimpleNamespace(id=99), fmt=fmt)
await Renamer(client, cfg or env.cfg, env.db).process(job)
return client, job
def attributes_by_type(kwargs):
return {type(a): a for a in kwargs["attributes"]}
async def auto(env, thumb=None, **changes):
"""The user's auto format (made on demand) with these changes and this thumbnail: what renames the files they just send."""
fmt = await ensure_auto(env.db, 42)
if changes:
await env.db.update_format(42, fmt["id"], **changes)
if thumb:
await env.db.set_format_thumb(42, fmt["id"], thumb)
return await env.db.get_format(42, fmt["id"])
# ---------------------------------------------------------------------------
# the streamed way
# ---------------------------------------------------------------------------
async def test_a_big_plain_rename_is_streamed_with_the_details_telegram_already_has(env):
message = FakeMessage(attributes=[video_attr()], thumbs=preview_sizes())
client, job = await rename(env, message)
assert client.relays == [(message.document, NEW_NAME, env.cfg.connections)]
assert client.downloads == [] and client.uploads == [] # nothing was saved, and nothing uploaded from a file
(target, input_file, kw), = client.sent
assert target == 42 and kw["reply_to"] == 7 and input_file.name == NEW_NAME
by_type = attributes_by_type(kw)
assert by_type[types.DocumentAttributeFilename].file_name == NEW_NAME
video = by_type[types.DocumentAttributeVideo]
assert (video.duration, video.w, video.h, video.supports_streaming) == (1400.5, 1920, 1080, True)
assert kw["force_document"] is False and kw["mime_type"] == "video/x-matroska" and kw["parse_mode"] == "html"
assert kw["thumb"] == PREVIEW and client.preview_downloads == 1 # the original's own preview, as no frame can be cut from a file
assert kw["caption"] == "<b>[S01E05] Tokyo Revengers [1080p]</b>"
assert job.state == "done" and (await env.db.totals())[0] == 1
assert "Streaming" in client.edits[0][0] # the progress message says what is going on
done, buttons = client.edits[-1]
assert "Done" in done and human_size(BIG) in done and buttons is None
assert ft.stats.streamed == 1 and ft.stats.stream_fallbacks == 0
assert not (Path(env.cfg.temp_dir) / "job1").exists()
async def test_the_formats_thumbnail_wins_and_the_original_preview_is_then_not_even_fetched(env):
client, _job = await rename(env, fmt=await auto(env, thumb=b"\xff\xd8mine"))
assert client.sent[0][2]["thumb"] == b"\xff\xd8mine" and client.preview_downloads == 0 and len(client.relays) == 1
fmt = await env.db.create_format(42, "One Piece")
await env.db.set_format_thumb(42, fmt["id"], b"\xff\xd8format")
client, _job = await rename(env, fmt=await env.db.get_format(42, fmt["id"]))
assert client.sent[0][2]["thumb"] == b"\xff\xd8format" and len(client.relays) == 1
async def test_a_format_streams_with_its_own_caption(env):
await auto(env, caption="<i>THE AUTO FORMAT'S {filename}</i>") # another format's caption is not borrowed
fmt = await env.db.create_format(42, "One Piece", template="One Piece - E{episode}")
await env.db.update_format(42, fmt["id"], caption="<b>{filename}</b> 路 {size} 路 {duration}")
client, _job = await rename(env, new_name="One Piece - E5.mkv", fmt=await env.db.get_format(42, fmt["id"]))
(_t, _f, kw), = client.sent
assert len(client.relays) == 1
assert kw["caption"] == f"<b>One Piece - E5</b> 路 {human_size(BIG)} 路 {human_duration(1400.5)}" # the duration is the one Telegram has
async def test_a_file_that_gets_no_thumbnail_anyway_is_streamed_without_one(env):
message = FakeMessage("Archive.zip", BIG, mime="application/zip")
client, _job = await rename(env, message, new_name="Archive 2.zip")
(_t, _f, kw), = client.sent
assert len(client.relays) == 1 and kw["thumb"] is None and client.preview_downloads == 0
assert kw["force_document"] is True and kw["mime_type"] == "application/zip"
assert [type(a) for a in kw["attributes"]] == [types.DocumentAttributeFilename]
async def test_an_original_sent_as_a_file_is_streamed_as_a_file(env):
client, _job = await rename(env, FakeMessage(), fmt=await auto(env, thumb=b"\xff\xd8mine")) # an .mkv without any video details: Telegram sent it as a file
(_t, _f, kw), = client.sent
assert len(client.relays) == 1 and kw["force_document"] is True and kw["thumb"] == b"\xff\xd8mine"
assert types.DocumentAttributeVideo not in attributes_by_type(kw)
async def test_audio_is_streamed_with_the_title_and_artist_telegram_has(env):
message = FakeMessage("old name.mp3", BIG, attributes=[audio_attr()], mime="audio/mpeg")
client, _job = await rename(env, message, new_name="New Song.mp3")
(_t, _f, kw), = client.sent
audio = attributes_by_type(kw)[types.DocumentAttributeAudio]
assert (audio.duration, audio.title, audio.performer) == (215, "Song", "Me") and not audio.voice
assert kw["force_document"] is False and kw["mime_type"] == "audio/mpeg" and kw["thumb"] is None and len(client.relays) == 1
async def test_a_forced_type_is_followed_when_telegram_has_the_details_for_it(env):
await env.db.update_settings(42, file_type="document")
client, _job = await rename(env) # sent as a video, but the user wants documents
(_t, _f, kw), = client.sent
assert len(client.relays) == 1 and kw["force_document"] is True and types.DocumentAttributeVideo not in attributes_by_type(kw)
async def test_the_caption_may_use_the_duration_when_telegram_has_it(env):
client, _job = await rename(env, fmt=await auto(env, caption="<b>{filename}</b> 路 {duration}"))
assert client.sent[0][2]["caption"] == f"<b>[S01E05] Tokyo Revengers [1080p]</b> 路 {human_duration(1400.5)}" and len(client.relays) == 1
async def test_a_streamed_file_goes_to_the_target_channel_like_any_other(env):
await env.db.update_settings(42, target_channel=-100123)
client, _job = await rename(env)
(target, _f, kw), = client.sent
assert target == -100123 and "reply_to" not in kw and "channel" in client.edits[-1][0]
async def test_a_rejected_caption_is_dropped_for_a_streamed_file_too(env):
message = FakeMessage(attributes=[video_attr()], thumbs=preview_sizes())
client = FakeClient(message, env.media_dir / "sample.mkv")
client.send_errors = [errors.MediaCaptionTooLongError(request=None)]
job = Job(id=1, user_id=42, chat_id=42, msg_id=7, file_name=NAME, size=BIG, new_name=NEW_NAME, status_msg=SimpleNamespace(id=99))
await Renamer(client, env.cfg, env.db).process(job)
(_t, _f, kw), = client.sent
assert kw["caption"] is None and "caption was rejected" in client.edits[-1][0] and job.state == "done" and len(client.relays) == 1
async def test_the_done_message_gives_the_speed_of_the_whole_stream(env, monkeypatch):
ticks = iter(range(0, 1000, 2)) # every look at the clock is two seconds later than the one before
monkeypatch.setattr(pipeline, "time", SimpleNamespace(monotonic=lambda: next(ticks)))
client, _job = await rename(env)
assert f"{human_size(BIG)} 路 猬団瑔 {human_size(BIG / 2)}/s streamed" in client.edits[-1][0]
async def test_a_stream_that_took_under_a_second_shows_no_speed(env):
client, _job = await rename(env)
assert "Done" in client.edits[-1][0] and "/s" not in client.edits[-1][0]
# ---------------------------------------------------------------------------
# when the file has to be looked at: the usual way (download, ffmpeg, upload)
# ---------------------------------------------------------------------------
def usual_way(client, job):
"""The file was saved and uploaded the usual way, and sent once."""
assert client.relays == [] and len(client.downloads) == 1 and len(client.uploads) == 1
assert len(client.sent) == 1 and job.state == "done"
assert ft.stats.streamed == 0 and ft.stats.stream_fallbacks == 0 # not streaming is not a failure
@needs_ffmpeg
async def test_small_files_are_not_streamed(env):
message = FakeMessage(size=ft.BIG_FILE_THRESHOLD, attributes=[video_attr()], thumbs=preview_sizes())
client, job = await rename(env, message)
usual_way(client, job)
@needs_ffmpeg
async def test_metadata_and_tracks_change_the_file_so_it_is_not_streamed(env):
await env.db.update_settings(42, metadata_on=True, meta={"title": "Tokyo Revengers"})
client, job = await rename(env)
usual_way(client, job)
@needs_ffmpeg
async def test_streaming_can_be_switched_off_and_needs_the_parallel_transfer(env):
for environ in ({"STREAM_RENAME": "0"}, {"FAST_TRANSFER": "0"}):
client, job = await rename(env, cfg=env.new_cfg(**environ))
usual_way(client, job)
@needs_ffmpeg
async def test_a_video_without_a_thumbnail_to_reuse_is_saved_so_that_one_can_be_cut_from_it(env):
for thumbs in (None, [types.PhotoStrippedSize(type="i", bytes=b"\x01\x02")]): # a blurred stripped preview is no thumbnail
message = FakeMessage(attributes=[video_attr()], thumbs=thumbs)
client, job = await rename(env, message)
usual_way(client, job)
assert client.sent[0][2]["thumb"][:2] == b"\xff\xd8" and client.sent[0][2]["thumb"] != PREVIEW # a frame of the video
@needs_ffmpeg
async def test_a_forced_video_or_audio_type_needs_the_details_only_the_file_can_give(env):
await env.db.update_settings(42, file_type="video")
client, job = await rename(env, FakeMessage(thumbs=preview_sizes())) # an .mkv sent as a file: no video details
usual_way(client, job)
assert types.DocumentAttributeVideo in attributes_by_type(client.sent[0][2]) # probed from the file instead
await env.db.update_settings(42, file_type="audio")
client, job = await rename(env, FakeMessage(attributes=[video_attr()], thumbs=preview_sizes()))
usual_way(client, job)
@needs_ffmpeg
async def test_a_caption_that_wants_the_duration_needs_the_file_when_telegram_does_not_know_it(env):
fmt = await auto(env, thumb=b"\xff\xd8mine", caption="<b>{filename}</b> 路 {duration}")
client, job = await rename(env, FakeMessage(), fmt=fmt) # sent as a file: no duration
usual_way(client, job)
duration = (await probe(env.media_dir / "sample.mkv")).duration
assert client.sent[0][2]["caption"] == f"<b>[S01E05] Tokyo Revengers [1080p]</b> 路 {human_duration(duration)}" # read from the file
@needs_ffmpeg
async def test_a_message_without_a_document_is_never_streamed(env):
client, job = await rename(env, FakeMessage(document=False))
usual_way(client, job)
# ---------------------------------------------------------------------------
# when streaming goes wrong: the usual way takes over
# ---------------------------------------------------------------------------
@needs_ffmpeg
async def test_a_failed_stream_falls_back_to_the_usual_way_and_says_so(env, log_channel):
client = FakeClient(FakeMessage(attributes=[video_attr()], thumbs=preview_sizes()), env.media_dir / "sample.mkv")
client.relay_error = RuntimeError("DC unreachable")
job = Job(id=1, user_id=42, chat_id=42, msg_id=7, file_name=NAME, size=BIG, new_name=NEW_NAME, status_msg=SimpleNamespace(id=99))
await Renamer(client, env.cfg, env.db).process(job)
await log_channel.flush()
assert len(client.relays) == 1 and len(client.downloads) == 1 and client.uploads == [(env.media_dir / "sample.mkv").read_bytes()]
(_t, _f, kw), = client.sent
assert kw["force_document"] is False and kw["caption"] == "<b>[S01E05] Tokyo Revengers [1080p]</b>"
assert job.state == "done" and (await env.db.totals())[0] == 1 # counted once
done = client.edits[-1][0]
assert "Done" in done and "Streaming didn't work for this file" in done
assert (ft.stats.streamed, ft.stats.stream_fallbacks, ft.stats.last_stream_fallback) == (0, 1, "RuntimeError: DC unreachable")
assert ft.stats.fallbacks == 0 # the parallel transfer itself did not fail
(report,) = log_channel.reports # the log channel is told, with the traceback
assert "streaming" in report and "RuntimeError: DC unreachable" in report and "Traceback" in report
assert not (Path(env.cfg.temp_dir) / "job1").exists()
@needs_ffmpeg
async def test_telegram_refusing_the_streamed_parts_also_falls_back(env):
client = FakeClient(FakeMessage(attributes=[video_attr()], thumbs=preview_sizes()), env.media_dir / "sample.mkv")
client.send_errors = [errors.FilePartMissingError(request=None, capture=3)]
job = Job(id=1, user_id=42, chat_id=42, msg_id=7, file_name=NAME, size=BIG, new_name=NEW_NAME, status_msg=SimpleNamespace(id=99))
await Renamer(client, env.cfg, env.db).process(job)
assert len(client.relays) == 1 and len(client.downloads) == 1 and len(client.uploads) == 1 and len(client.sent) == 1
assert job.state == "done" and "Streaming didn't work" in client.edits[-1][0] and (await env.db.totals())[0] == 1
assert (ft.stats.streamed, ft.stats.stream_fallbacks) == (0, 1) and "FilePartMissingError" in ft.stats.last_stream_fallback
async def test_any_other_refusal_after_streaming_fails_the_job_without_a_second_try(env):
client = FakeClient(FakeMessage(attributes=[video_attr()], thumbs=preview_sizes()), env.media_dir / "sample.mkv")
client.send_errors = [errors.ChatWriteForbiddenError(request=None)]
job = Job(id=1, user_id=42, chat_id=42, msg_id=7, file_name=NAME, size=BIG, new_name=NEW_NAME, status_msg=SimpleNamespace(id=99))
await Renamer(client, env.cfg, env.db).process(job)
assert job.state == "failed" and client.sent == [] and client.downloads == [] and len(client.relays) == 1 # no download/upload all over again
assert "Failed" in client.edits[-1][0] and ft.stats.stream_fallbacks == 0 and (await env.db.totals())[0] == 0
async def test_cancelling_a_stream_cancels_the_job_and_does_not_fall_back(env):
client = FakeClient(FakeMessage(attributes=[video_attr()], thumbs=preview_sizes()), env.media_dir / "sample.mkv")
client.relay_error = Cancelled()
job = Job(id=1, user_id=42, chat_id=42, msg_id=7, file_name=NAME, size=BIG, new_name=NEW_NAME, status_msg=SimpleNamespace(id=99))
await Renamer(client, env.cfg, env.db).process(job)
assert job.state == "cancelled" and client.sent == [] and client.downloads == [] and "Cancelled" in client.edits[-1][0]
assert ft.stats.stream_fallbacks == 0 and ft.stats.streamed == 0
async def test_a_cancel_that_arrives_as_the_stream_ends_sends_nothing(env, monkeypatch):
client = FakeClient(FakeMessage(attributes=[video_attr()], thumbs=preview_sizes()), env.media_dir / "sample.mkv")
job = Job(id=1, user_id=42, chat_id=42, msg_id=7, file_name=NAME, size=BIG, new_name=NEW_NAME, status_msg=SimpleNamespace(id=99))
real_relay = ft.relay
async def relay_then_cancel(*a, **kw):
result = await real_relay(*a, **kw)
job.cancel.set()
return result
monkeypatch.setattr(ft, "relay", relay_then_cancel)
await Renamer(client, env.cfg, env.db).process(job)
assert job.state == "cancelled" and client.sent == []
# ---------------------------------------------------------------------------
# helpers
# ---------------------------------------------------------------------------
def test_source_attributes_ignore_round_videos_and_voice_notes():
plain, round_video = video_attr(), video_attr(round_message=True)
song, voice = audio_attr(), audio_attr(voice=True)
assert source_attributes(FakeMessage(attributes=[plain, song])) == (plain, song)
assert source_attributes(FakeMessage(attributes=[round_video, voice])) == (None, None)
assert source_attributes(FakeMessage(attributes=[types.DocumentAttributeFilename("a.mkv")])) == (None, None)
assert source_attributes(FakeMessage(document=False)) == (None, None) and source_attributes(object()) == (None, None)
def test_make_caption_fills_in_what_it_is_given():
text = make_caption("<b>{filename}</b> 路 {original} 路 {size} 路 {duration} 路 {ext} 路 {nonsense}", new_name="New - E5.mkv",
orig_name="[Grp] Show - 05 [720p].mkv", ext=".mkv", size=3 * MIB, duration=3725)
assert text == f"<b>New - E5</b> 路 [Grp] Show - 05 [720p].mkv 路 {human_size(3 * MIB)} 路 {human_duration(3725)} 路 mkv 路 {{nonsense}}"
assert make_caption("{duration}", new_name="a.mkv", orig_name="a.mkv", ext=".mkv", size=1, duration=0) is None # empty: no caption
assert len(make_caption("x" * 5000, new_name="a.mkv", orig_name="a.mkv", ext=".mkv", size=1, duration=0)) == 1000
def test_make_caption_pads_the_episode_when_asked_to():
text = make_caption("Ep {episode} / {1episode} / {2episode} / {3episode}", new_name="x.mkv",
orig_name="Show - 7 [720p].mkv", ext=".mkv", size=1, duration=0)
assert text == "Ep 07 / 07 / 007 / 0007"
assert make_caption("{3episode} {3episode}", new_name="x.mkv", orig_name="Show - 321 [720p].mkv", ext=".mkv", size=1, duration=0) == "0321 0321"
assert make_caption("{3episode}", new_name="x.mkv", orig_name="Show - 1321 [720p].mkv", ext=".mkv", size=1, duration=0) == "1321"
assert make_caption("<b>{2episode}</b>", new_name="x.mkv", orig_name="Plain.mkv", ext=".mkv", size=1, duration=0) == "<b></b>"
async def test_the_embedded_preview_is_only_fetched_when_it_is_worth_having():
class Client:
def __init__(self, data=PREVIEW, error=None):
self.data, self.error, self.calls = data, error, 0
async def download_media(self, msg, file=None, thumb=None):
self.calls += 1
if self.error:
raise self.error
return self.data
stripped = [types.PhotoStrippedSize(type="i", bytes=b"\x01\x02")]
client = Client()
assert await embedded_thumb(client, FakeMessage(thumbs=preview_sizes())) == PREVIEW
assert await embedded_thumb(client, FakeMessage(thumbs=stripped)) is None and client.calls == 1 # blurred: not asked for
assert await embedded_thumb(client, FakeMessage(thumbs=stripped), allow_stripped=True) == PREVIEW
assert await embedded_thumb(client, FakeMessage(thumbs=None)) is None and await embedded_thumb(client, object()) is None
assert await embedded_thumb(Client(error=OSError("gone")), FakeMessage(thumbs=preview_sizes())) is None
assert await embedded_thumb(Client(data=b""), FakeMessage(thumbs=preview_sizes())) is None
|