Skip to content

feat(v2/ezytel): inline images as base64 data URIs by default#128

Open
hiddifyafk wants to merge 1 commit into
hiddify:mainfrom
hiddifyafk:feat/ezytel-inline-base64
Open

feat(v2/ezytel): inline images as base64 data URIs by default#128
hiddifyafk wants to merge 1 commit into
hiddify:mainfrom
hiddifyafk:feat/ezytel-inline-base64

Conversation

@hiddifyafk
Copy link
Copy Markdown

Summary

Follow-up to #127. The three image surfaces in v2/ezytel now hand back ready-to-render data:image/jpeg;base64,… URIs by default, so a JSON / HTML client can paint thumbnails with no second ProxyImage round-trip.

  • GetChannelInfo.avatar_path — was a server-side cache/<md5>.jpg path that a remote client could not resolve. Now a data: URI.
  • GetChannelMessages.html<img src> and background-image:url(...) carried proxy.php?url=<hex> placeholders the client had to intercept. Now resolved server-side and replaced inline.
  • GetChannelMessages.channel_avatar — same treatment.

ProxyImage is unchanged and still works as the per-image escape hatch.

Wire change

Both requests gain bool disable_inline_images = 3;. Field name is inverted on purpose — proto3 cannot distinguish unset from false, so leaving the new field at its zero value (the default) gives the desired inline behavior. Set it to true to opt back into the legacy cache/<md5>.jpg / proxy.php?url=<hex> form.

Implementation notes

  • Refactored the cache+download tail of ProxyImage into a private fetchImageBytes shared by ProxyImage, GetChannelInfo, and GetChannelMessages.
  • New dataURL(bytes, contentType) helper.
  • GetChannelMessages resolves placeholders with a bounded 8-worker fan-out, then substitutes via a single strings.NewReplacer sweep. Per-image fetch failures leave the placeholder so a single broken image does not fail the whole page.
  • The persisted <chid>.json snapshot keeps the cache/<md5>.jpg form so the cache file stays small. The stale-replay path re-encodes from the on-disk JPEG when a caller wants inline.

Test plan

  • go build ./v2/ezytel/... — clean
  • go vet ./v2/ezytel/... — clean
  • go test ./v2/ezytel/... -count=1 -v — 12/12 pass: 7 pre-existing tests plus TestDataURL, TestExtractProxyHexes, TestInlineProxyPlaceholders (concurrent path with hit count), TestInlineProxyPlaceholderSingle, TestDisableInlineImagesNoFetch.
  • End-to-end smoke (run on a live host):
    • grpcurl -plaintext -d '{"channel_id":"durov"}' 127.0.0.1:50051 ezytel.Ezytel/GetChannelInfoavatarPath starts with data:image/jpeg;base64,; piping through base64 -d produces a valid JPEG.
    • grpcurl -plaintext -d '{"channel_id":"durov"}' 127.0.0.1:50051 ezytel.Ezytel/GetChannelMessageshtml contains data:image/jpeg;base64,… and zero proxy.php?url=; rendered in a browser shows thumbnails with no further requests.
    • {"channel_id":"durov","disable_inline_images":true} reproduces today's proxy.php?url=<hex> form.

Notes / follow-ups

  • Inline payloads grow ~33 % vs. raw bytes; a busy GetChannelMessages page may exceed gRPC's 4 MB default. If real pages hit that ceiling, bump grpc.MaxSendMsgSize (server) and MaxCallRecvMsgSize (client) at the registration site in v2/hcore/grpc_server.go. Not changed here pending real-world measurement.
  • Per-image failure is silent; a debug log could be added later.

GetChannelInfo and GetChannelMessages now embed avatars and post
images as data:image/jpeg;base64,… URIs directly in their JSON/HTML,
so a client can render them with no second ProxyImage round-trip.
Adds disable_inline_images=true on both requests as an opt-out
that preserves the legacy "cache/<md5>.jpg" / "proxy.php?url=<hex>"
form. Image fetches reuse the existing translate.goog front and
disk cache; GetChannelMessages fans out to 8 workers per page.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants