mtflute 0.2.3
mtflute: ^0.2.3 copied to clipboard
Pure-Dart Telegram MTProto client (API layer 227) with a built-in HTTP streaming engine for mediakit / video players.
Changelog #
0.2.3 #
- Tuned
downloadRangeconcurrency: 4 connections x 4 pipelined requests each, retuned_countWorkersthresholds. Balances throughput against the DC's connection-drop anti-abuse on high-latency mobile paths.
0.2.2 #
Fixes video playback stability + eliminates player jank. Validated end-to-end with ffprobe/ffmpeg against a real streamed file (clean decode mid-file, no corruption, faster than realtime).
- Off-isolate decryption. Large ciphertexts (file chunks, ≥128 KB) now
decrypt in a persistent worker-isolate pool (
decrypt_pool.dart) instead of on the main isolate. The AES-IGE block loop no longer blocks UI rendering / the local stream server — measured main-isolate stalls dropped from ~90 ms to ~38 ms during sustained streaming._processIncoming/deserializeEncryptedgained async variants; small messages still decrypt inline. Dispatch is by msgId so the isolate hop cannot reorder RPC results. - Reconnect no longer crashes downloads. Transient "pending request invalidated" reconnect errors are retried instead of surfacing as an unhandled async exception (which previously killed the serving connection — the "nothing plays" symptom for HTTP-streamed media).
0.2.1 #
Streaming throughput + smoothness fixes (benchmarked on a high-RTT mobile path).
downloadRange / worker pool #
- Per-connection request pipelining (up to 3 concurrent
upload.getFileper connection, demuxed by msgId) — the previous 1-request-per-worker model capped throughput at ~1 MB/s on high-RTT paths. Measured ~10x improvement (0.3 → 3+ MB/s sustained). Connection count kept low because DCs drop many simultaneous fresh connections; pipelining supplies the concurrency instead. - Transient reconnect errors ("reconnect: pending request invalidated", "Not connected", timeouts) are now retryable in the part-fetch loop instead of fatally aborting the whole range download.
Future.wait(eagerError: false)on part fetches so a fast-failing part can't orphan sibling in-flight parts (which previously surfaced as an unhandled async exception during a reconnect storm)._countWorkersretuned; retries per part raised to 20 with capped backoff.
crypto #
- AES-IGE encrypt/decrypt rewritten to be allocation-free per block (was
~3
Uint8Listallocations per 16-byte block → ~96k allocations + heavy GC per 512 KB chunk on the single Dart isolate, a major source of video-player jank / frozen spinners). Verified byte-identical to the previous output.
0.2.0 #
Streaming engine release. Three deep-audit rounds against a gogram reference (150+ raw findings, ~80 confirmed, ~50 fixed).
Layer + codegen #
- Bumped Telegram API to layer 227 (from TDesktop dev).
- Fixed
tool/tlgen.darthandling of{X:Type}generics and!Xtemplate refs.
File streaming #
- New
client.downloadStream(location)returnsStream<Uint8List>(in-order, parallel workers under the hood). - New
client.downloadRange(location, start, end)for arbitrary byte ranges with parallel workers, FILE_MIGRATE + FILE_REFERENCE_EXPIRED handling. - New
client.streamMessage(peer, msgId)andclient.downloadMessageRange(peer, msgId, start, end)— resolve media via the built-in cache and yield bytes. - New
TelegramFileStreamServer— local HTTP server that publishes any Telegram file as a URL with full HTTP Range 206 support, HEAD, CORS, seek-aware prefetch, per-entry LRU chunk cache, and per-request cancellation on client disconnect. Feed the URL tomediakit/video_player/<video>. server.publishMessage(peer, msgId)returns a stable URL; the chunk cache lives across HTTP requests.server.warmup(dcId, workers)pre-opens cross-DC senders so first fetch doesn't pay exportAuth latency.MediaLocationCache— LRU keyed by(peerId, msgId)→ResolvedMedia(location, size, mime, dcId, fileName, duration)with 20h TTL under Telegram's 24hfile_referencewindow.
Session + peer persistence #
- Peer cache (users, channels, chats, usernames) now serialized inside
SessionDataand reloaded on next start — no more re-resolve after app restart. - Atomic session write (unique tmp filename + POSIX rename, no delete-then-rename data-loss window).
- Async debounced flush (2s window) + guaranteed flush on
close(). - String session format now byte-compatible with gogram (
1BvE+ RawURL base64 JSON; legacy1BvXalso parsed correctly for binary keys via latin1). SessionData.dcIdalways round-trips.
Reconnect + transport #
_ensureReadywaits for in-flight reconnect (bounded by clienttimeout); no more racing double-dials._reconnectis single-flight, uses persisted auth key, exponential backoff with jitter.- Ping is fire-and-forget — no leaked pending completers.
bad_msgcodes 32/33/48 drain all pending requests and letinvoke()transparently retry on the fresh session.bad_server_salttransparently retries only the affected request (was killing all pending).- Terminal auth errors (AUTH_KEY_UNREGISTERED, etc.) disable autoReconnect; a subsequent successful
loginBot/signIn/checkPasswordre-enables it. - Transport now tracks
lastReadAt;_ensureReadyforce-reconnects if the socket has been idle >90s (catches silently-dead sockets after Android backgrounding). - Transport
close()wakes stuck reads instead of hanging. - Read-idle timeout on transport (75s) surfaces silently-dead peers.
File upload/download #
- Upload closes RAF only after all worker futures resolve (was closing while siblings still reading).
chunkSizevalidated as power-of-2 multiple of 1024 that divides 512 KB.- FLOOD_WAIT respected per part.
- Retries bounded, non-retryable errors (StateError, UnimplementedError) fail fast instead of looping 5 times.
- Parallel
downloadStreamstops correctly on last chunk (not on any short chunk).
Crypto + TL correctness #
- TL strings are now UTF-8 (was UTF-16 code units — broke every non-ASCII payload).
bigIntToBytesfixed: big-endian left-pad, high-order trim (was truncating low bytes / padding wrong side).auth_keyleft-padded to exactly 256 bytes.writeBytesrefuses payloads > 16 MiB instead of silently truncating.
Android/Flutter #
- No default relative
sessionFile— you must pass an absolute path (frompath_provider). - Login callback prompts throw a clear error on mobile instead of using stdin.
- SIGINT handler uses logger instead of print.
- Everything runs on
dart:ioonly — nodart:mirrors/dart:ffi/isolates. Seeexample/flutter_engine.dartfor a ready-to-dropTgEnginesingleton.
Cache correctness #
- Access hash preserved when server sends minimal peer without hash.
- Reverse username mappings pruned on rename.
- LRU eviction (default 100k users / 20k channels) prevents unbounded growth in long-running engines.
getDifferencebacks off on FLOOD_WAIT instead of hammering every 3s.
0.1.3 #
- Fixed DC migration loop: migrate now kills the old poll loop before reconnecting, preventing duplicate pollers and repeated migrations.
- Migration depth capped at 5 (matching gogram) to prevent infinite recursion.
- Poll loop and reconnect logic bail immediately during active migration instead of racing.
0.1.2 #
- Fixed reconnect-spam loop: long-poll TCP reads no longer time out at 15s and trigger false reconnects.
- Reconnect is now single-flight (mutex-guarded), capped at 20 attempts, with exponential backoff (1s → 30s max).
- Poll loop runs once per client lifetime — no more concurrent pollers spawned per reconnect.
0.1.1 #
- Shorter pub.dev description.
- Replaced stub example with a real bot quickstart (inline buttons + callback handler).
- Bumped
pointycastleto^4.0.0andlintsto^6.0.0. - Added dartdoc coverage on generated TL types and the main client surface.
0.1.0 — first publish #
Initial release. Pure-Dart MTProto client against Telegram API layer 225.
- Auth: bot tokens, phone + OTP + 2FA (SRP), string + file sessions
- Messaging: send/edit/delete/forward/pin, Markdown + HTML parse modes, inline button builder
- Updates: socket push + getDifference polling, dedup, auto-reconnect with backoff
- Files: multi-worker parallel up/download, cross-DC sender pool with caching
- DC migration: transparent on USER/PHONE/FILE/NETWORK_MIGRATE
- Conversation API, channels admin ops, participant iteration, bot commands, callback/inline answers
- Logger with TRACE/DEBUG/INFO/WARN/ERROR levels
- 64 tests: 55 unit + 9 live against Telegram