Files
Piik/cmd/piik-app/README.md
T
AAA 799d6efb4a fix: improve sharing recovery and UI feedback
## Problem and outcome / 问题与结果

Browser sharing can remain connected to a local encoding transport that never becomes usable, leaving viewers without frames. This phase bounds that startup at eight seconds and restores ordinary encoding through the existing failure path. It also keeps ready App services running after a browser-open failure, preserves user presentation/playback choices, and aligns room feedback, illustrated hints, metrics and documentation.

部分环境下,本机编码连接一直无法建立会让观众收不到画面。本轮在八秒后进入现有降级流程,同时修复客户端启动移交、播放状态和前端反馈的一致性问题,并更新官网与中英文教程。

Closes #399.

Related: #396. The release contains relevant startup/error-handling repairs, but the reporting machine has not been retested. Keep this issue open for a current-build result. #401 (optional Windows capture-border hiding) remains deferred.

Release / 发布版本:**v1.3.1**.

## Release notes

### 中文

- 客户端记住上次确认的启动模式;启动移交保留已保存的语言、纯视觉模式和主题选择。
- 系统浏览器未能自动打开时,客户端继续运行,并在终端保留页面地址、重试入口和具体错误。
- 改善浏览器共享画面的启动兜底,避免 App 媒体因短暂信令中断被误判为失败。
- 音轨变化保持现有视频与观众的本机暂停状态;连接恢复和暂停恢复不再重复播放电视开机动画。
- 修复昵称编辑及长昵称查看,减少重复的角色提示,统一人物选择和连接状态的区别。
- 完善房间准入、密码和邀请说明;复制反馈对应当前链接及当前操作,避免迟到结果覆盖新反馈。
- 修正采集参数、统计单位与采样区间的展示,未知参数不再用配置上限代替。
- 统一提示漫画、状态图标与动效,改善选源器还焦、键盘操作、剧场布局和触控目标尺寸。
- 服务端默认支持 20 位观众,可在配置中设为 1–20 位,房主不占观众名额。
- 更新官网、教程入口和中英文说明,清理过时预览及失用代码。

兼容说明:使用旧版 App 连接更新后的站点时,站点会优先保留自己的显示偏好。如需更改语言或主题,请在进入后的页面调整。

### English

- Remember the last confirmed App launch mode and preserve saved language, visual-mode and theme choices during launch handoff.
- Keep the App running if the system browser fails to open, with the page address, retry action and error details available in the terminal.
- Improve the browser sharing startup fallback and avoid treating brief signaling outages as App media failures.
- Keep existing video and local pause state when audio tracks change. Reconnecting and resuming no longer replay the television startup animation over the picture.
- Fix nickname editing and full-name access, remove redundant role hints, and distinguish participant selection from connection state.
- Clarify room access, passwords and invitations. Copy feedback now belongs to the current link and operation, so late results cannot replace newer feedback.
- Correct capture details, metric units and observation intervals. Unknown capture values are no longer replaced with configured ceilings.
- Align illustrated hints, status icons and motion. Improve source-picker focus, keyboard operation, theater layout and touch targets.
- Default the Server to 20 viewers, configurable from 1 to 20. The host does not count toward that limit.
- Update the website, guide links and bilingual instructions, and remove obsolete previews and unused code.

Compatibility: when an older App connects to an updated Site, the Site keeps its saved presentation preferences. Change language or theme on the destination page when using an older launcher.

## Verification / 验证

Non-publishing [CI candidate run 34919320511](https://github.com/TNTcraftHIM/Piik/actions/runs/34919320511) passed Go core, race, Web checks, Server/container verification and all three native App package checks at source 188376e8. Subsequent commits only update deployment documentation, status and TODO; documentation/hygiene checks passed. Product inputs are unchanged.

- `npm run check`: TypeScript, 64 test files / 854 tests, and the Web production build passed.
- `npm run check:client-core`: final Go source passed tests, formatting, vet and core target builds. Windows capture compilation and geometry checks passed earlier in this phase; later edits did not change native capture.
- Website build, documentation checks, repository hygiene and diff checks passed. Existing documentation-size and bundle-size warnings remain.
- Actual browser flows covered sharing, source changes, pause/resume, late audio, brief signaling loss, invitation/copy feedback, entry preferences, source-picker focus, and narrow layouts.
- A local three-viewer P2P relay check covered quality-report expiry, recovery and cleanup without interrupting video. A controlled browser-helper failure returning `0xc0000005` left the real App launcher and Local service available.
- Homepage checks covered Chinese/English, light/dark preferences and inline-film open/close after removing the separate-page entry.

Limits: no new reporter-machine confirmation for #396 or the Windows URL-handler crash; physical Safari/mobile, macOS/Linux capture and wider public-network/endurance coverage remain deferred. Formal release artifacts will be built from the approved squash-merge revision. Hero-loop controls and video-adjacent tooltip placement remain open design items in TODO.

## Impact / 影响

Compatible patch; signaling, Native control and capture contracts are unchanged. Older App launch handoff keeps the updated Site's saved presentation preferences. The Server's default viewer limit is corrected to 20; an explicit 1–20 configuration remains valid.

Service updates retain persisted room authority but restart active media sessions. Deploy the verified immutable Server artifact through the existing bounded health/recovery process. Public Demo remains open and P2P-only; deployment does not enable SFU or change infrastructure/configuration.
2026-09-15 10:13:29 +08:00

17 KiB

Piik App

English · 简体中文 · Getting started

Piik App provides local rooms and native screen capture through the Piik web interface in your system Browser.

Run A Package

Extract the matching platform bundle in full and keep runtime beside the App executable. Run piik-app.exe on Windows, ./piik-app on Linux, or Piik App.app on macOS. The launcher opens in the system Browser. Linux native capture uses the system dependencies described in the Linux capture guide.

If the page does not open automatically, open the address shown in the terminal or press O there to retry. Keep the App running while using that page.

Windows App and browser sharing are the primary tested paths. The macOS and Linux apps have not yet been tested on physical devices; test results and feedback are welcome. macOS native capture requires Apple silicon and macOS 13 or newer. Package construction and public Release publication are separate steps in deployment.

Modes

  • With no mode argument, the App opens a small launcher in the system Browser. It selects Local, a temporary public HTTPS invitation, or a saved Site, then enters the normal Host page.
  • The launcher remembers the last mode confirmed with Open Piik and the saved Site address. Without a saved mode, it selects Site when an address is saved, otherwise Public invite; it waits for confirmation before starting. App configuration owns the saved settings. The App RPC starts before this choice and accepts the saved Site alongside the Local Host origin. An App-opened Site remembers the opt-in in that Browser origin; later manually opened Host pages may reuse the running App.
  • The --site, --local, and --link flags select a mode for CI and development.
  • The default launcher checks official releases after it opens, using GitHub first and Gitee if GitHub is unavailable. It opens the matching platform ZIP when available, falling back to the release page. It does not install or replace the App.

Local mode uses memory-only rooms, P2P relay, no SFU listener, and no public discovery. Ordinary Local works on a reachable LAN. The Public invite mode runs the packaged Cloudflare Tunnel sidecar for the existing HTTP/WebSocket control surface and uses one ordinary public STUN destination plus two bounded public survey destinations. Browser and Native edges share the same prediction rule; Native STUN, ICE checks, and media use one Pion UDP mux. Media does not travel through the HTTP tunnel, and a difficult media path still has no SFU or TURN fallback.

For Local invitations, the App automatically selects a sole active address or sole private IPv4. If several addresses remain possible, choose an interface and IP in the launcher. Public invite and Site mode need no LAN selection. When bypassing the launcher with --local, use --lan-address <address> to resolve an ambiguous choice. The selected address is checked again at startup; it affects Local invitation links, while ICE selects media paths independently.

The system Browser remains the Host UI. An App-launched Host offers the Browser's standard capture picker and a list of exact platform capture targets; the user selects one explicitly. Windows offers the same VP8/Auto/H264 selector: VP8 uses the pinned WebRTC/libvpx encoder and H264 uses hardware Media Foundation inside WebRTC's output pipeline. Auto measures target-profile encoding work before choosing one codec for the share. Windows uses Graphics Capture and WASAPI; macOS uses ScreenCaptureKit, VideoToolbox, and AudioToolbox; Linux delegates selection to the ScreenCast Portal and uses the system PipeWire/GStreamer hardware path. Video and audio share the same room route and PeerConnection. Native capture starts with the Host's current resolution, frame-rate, video/audio bitrate, and quality preference; live changes replace only the capture/encoder generation behind those stable connections. A Native edge with public STUN also attempts one bounded PCP, UPnP, or NAT-PMP mapping for its Pion UDP socket; pure LAN does not. Routers without a mapping service continue with ordinary ICE/STUN. The mapping does not create a relay or carry media through the App control link. A configured Site may receive a direct Native publication from the shared encoded source; Local and one-link modes remain P2P-only. An ordinary Web Host keeps the Browser capture path without probing the App.

Native P2P edges and embedded SFU use one shared Pion/LiveKit media adapter for feedback, forwarding allocation, bounded pacing and recovery. Native parents reuse suitable H.264/VP8 outputs and derive a missing lower output only for direct-child demand. Compatible children share that output; each edge receives only its selected representation. SFU publication combines its requested output prefix with one aggregate upstream budget. A lower-output constraint does not replace the original input or higher sibling outputs. The route controller owns persistent quality evidence and route replacement. See media quality for the implemented behavior and status for its acceptance limits.

The App configuration keeps an optional Local site passphrase. Leave it blank for an open Local site, or enter a password in the launcher before starting. When present, the App passes it to its own Host page in a URL fragment; the page uses the existing SiteAccess endpoint and removes the fragment before continuing. Viewer invitations keep using the existing room-scoped grant.

The terminal shows the current mode, entry links and startup state; its language follows the launcher and App-enabled pages. Press o to reopen the Browser, or q / Ctrl+C to end Local rooms and stop the local server and temporary public link. Plain-text output uses Ctrl+C. The App keeps running after a Site tab closes. When launched in its own Windows console, a startup or runtime error leaves the error visible until Enter is pressed. Normal shutdown, existing terminals and redirected or automated runs exit directly.

For one-link Internet sharing, open the App launcher, choose Public invite, create a room in the opened Browser, and send its normal invitation link. The random trycloudflare.com origin lasts only for that App run; a later launch creates a new temporary link. Cloudflare Quick Tunnels provide no uptime guarantee; use a configured Site when persistent control availability or SFU fallback matters.

Troubleshooting

Chromium WebRTC Connections

Why can screen capture succeed while the media connection fails?

Chromium-based Browsers can restrict WebRTC UDP through Browser settings, extensions or managed policies. Disabling non-proxied UDP can prevent even the local Browser-to-App media connection; successful capture or page loading does not prove that this separate connection is available.

Check the Browser's WebRTC/IP-handling policy and any extension's WebRTC or IP-leak protection setting. Restore a policy that permits WebRTC UDP, reload Piik, and verify that another extension or managed policy has not overridden the choice. Setting names and availability differ between Browsers. For example, Vivaldi exposes Settings > Privacy and Security > WebRTC IP Handling > Broadcast IP for Best WebRTC Performance. Changing this policy can expose network addresses to WebRTC peers; do not disable unrelated protections.

See Chromium's extension policy API, Vivaldi's setting example, and the verified policy mechanism and field case.

Diagnostics

Enable Debug launch in the mode selector before opening Piik, reproduce the problem, then press D in the interactive terminal to export a local ZIP. Use --debug for failures before the selector opens. Exporting does not stop the share or upload the archive. For Browser diagnostics, click the Debug chip icon after the theme control, confirm the reload, then use Web report to download. For cooperation failures, include both reports from the same reproduction. The diagnostic reference owns log locations, export commands, retention and privacy boundaries.

Development

Use the Node/npm/Go versions in Run from source. The binary embeds the Browser assets, so build them from the repository root first:

npm ci
npm run build:client

On Linux or macOS:

go run ./cmd/piik-app

On Windows, build and run from a stable executable path for the system firewall:

go build -o build/dev/piik-app.exe ./cmd/piik-app
./build/dev/piik-app.exe

These commands build the App only. Choose Local room or a configured Site for Browser capture. Native capture and Public invite need their helpers; use --capture-process / --tunnel-process to select built helpers, or follow packaging for a complete bundle.

The repository-level entry used locally and by CI is:

npm run check:client

On Windows, its generated App, capture, and media-test executables are written to the ignored repository build/client-check directory and reused on the next run. This keeps the executable identity stable for the system firewall; the files are local build output and are never packaged or committed.

It runs Go formatting, unit tests, vet, and Windows/Linux cgo-free cross-builds. The Darwin App and peer gate build only on macOS with cgo enabled and an installed SDK; other hosts report that skipped platform explicitly. The pinned media dependency's Darwin CPU statistics use Mach APIs through cgo, so a Windows or Linux core check does not establish Darwin build acceptance. The check compiles the current platform's isolated capture process and validates its bounded capability response. macOS additionally encodes one in-memory hardware H.264 IDR; Linux probes the Portal/PipeWire/GStreamer adapter. Real capture, GPU attribution, Browser decode, and public-network paths remain explicit physical gates rather than environment-dependent unit tests.

The loopback service binds IPv4 loopback on the first available port from 39721 through 39730. /health discovers the current process; /control admits at most two independent strict v9 control sessions. After hello, each session may list local capture choices, own one generation-fenced Host share, or receive one native Viewer source and its bounded encoded child edges. Closing one session retires only its resources. Its public instanceToken distinguishes the discovered process but is not authentication. The shared Go service owns room authority; the Browser owns the room protocol client and orchestrates native media through this control session. Viewer receive/relay remains available even when this machine has no accepted native capture encoder. Capture sidecars must match the App's current probe/encoded-output contract; the package-candidate wrapper validates that version before accepting its artifact.

Packaged builds report the product version and source SHA in the terminal and diagnostic context. The launcher uses that identity to distinguish a newer version, a different build of the same version, and an official release offered to a development build; the link never installs or interrupts a share. Versioning owns these meanings and GitHub operations owns automatic publication.

Packaging

From a clean revision, build the application release and run the platform's candidate wrapper on its native operating system. The wrapper builds capture, verifies the pinned public-link helper, and assembles and checks the App:

node scripts/package-app-release.mjs /outside/repository/app-release
node scripts/package-client-candidate.mjs /outside/repository/app-release windows-amd64 /outside/repository/client-candidate

Supported targets are windows-amd64, linux-amd64, and darwin-arm64. Use the matching target name in the command above. Darwin assembly requires a native macOS runner with its SDK and enables cgo; Windows and Linux assembly keep cgo disabled. The result is a ZIP bundle and SHA-256 file. Manual sidecar assembly, explicit CI packaging, Release publication and updates are documented in deployment; creating a candidate does not publish it.

The extracted bundle contains:

piik-app[.exe]
REVISION
LICENSE
THIRD-PARTY-NOTICES.txt
runtime/native/piik-capture[.exe] # supported native-media packages
runtime/tunnel/cloudflared[.exe] # packages that support --link

The executable embeds the Browser assets of the consumed application release and carries that same full Git revision as the package REVISION. App/Site interoperability follows the public compatibility contract.

The Windows App embeds the shared Piik mark through the cmd/piik-app/piik_windows_amd64.syso resource; the platform suffix keeps that Windows resource out of Linux and macOS builds. Linux packages include the standard share/applications desktop entry and hicolor icon. macOS packages include a thin Piik App.app launcher with an ICNS resource; the raw Go executable remains available beside it.

For a native Host smoke run, launch the App and select a window in the Host page. The App never guesses among multiple targets. The page still creates the room and sends the current SDP/ICE through the selected authority. A configured Site supplies its normal Internet routing and any enabled SFU fallback. Without a Site, the Public invite choice exposes the Local control surface while media remains P2P-only.

Gates

The Local gate starts the packaged process, loads a real built page in Chromium, proves bootstrap access and LAN invitation construction, and verifies complete process/profile cleanup. The loopback gate separately proves Site access with and without Chromium Local Network Access permission. Environment syntax below is POSIX; use equivalent variables on Windows.

PIIK_CLIENT_LOCAL_GATE=true \
CHROME_PATH=/path/to/chrome \
PIIK_CLIENT_EXE=/path/to/piik-app \
PIIK_CLIENT_GATE_LAN_ADDRESS=192.168.1.10 \
npm run gate:client-local

PIIK_CLIENT_LOOPBACK_GATE=true \
CHROME_PATH=/path/to/chrome \
PIIK_CLIENT_EXE=/path/to/piik-app \
npm run probe:client-loopback

PIIK_CLIENT_MEDIA_GATE=true \
CHROME_PATH=/path/to/chrome \
PIIK_CLIENT_GATE_STUN_URLS=stun:<stun-host>:3478 \
npm run gate:client-media

PIIK_CLIENT_NATIVE_HOST_GATE=true \
CHROME_PATH=/path/to/chrome \
PIIK_GO=/path/to/go \
npm run gate:client-native-host

PIIK_CLIENT_NATIVE_HOST_GATE=true \
PIIK_CLIENT_CROSS_NAT_GATE=true \
CHROME_PATH=/path/to/chrome \
PIIK_GO=/path/to/go \
PIIK_REMOTE_HOST=<public-test-host> \
PIIK_REMOTE_USER=<ssh-user> \
PIIK_REMOTE_SSH_KEY=/path/to/key \
PIIK_CLIENT_GATE_STUN_URLS=stun:<stun-host>:3478 \
npm run gate:client-native-host

PIIK_CLIENT_NATIVE_HOST_GATE=true \
PIIK_CLIENT_LINK_MEDIA_GATE=true \
CHROME_PATH=/path/to/chrome \
PIIK_GO=/path/to/go \
PIIK_CLOUDFLARED=/path/to/cloudflared \
PIIK_REMOTE_HOST=<public-test-host> \
PIIK_REMOTE_USER=<ssh-user> \
PIIK_REMOTE_SSH_KEY=/path/to/key \
npm run gate:client-native-host

PIIK_CLIENT_LINK_GATE=true \
PIIK_GO=/path/to/go \
PIIK_CLOUDFLARED=/path/to/cloudflared \
PIIK_REMOTE_HOST=<public-test-host> \
PIIK_REMOTE_USER=<ssh-user> \
PIIK_REMOTE_SSH_KEY=/path/to/key \
npm run gate:client-link

The loopback health response reports video, process-audio, system-audio, and hardware H.264 availability separately. The Windows media gate proves one hardware-H.264 capture generation, shared Pion source, Browser decode, PLI recovery, and STUN candidate gathering. The native Host gate proves room creation and native video delivery through the current route; native audio is included when the capability probe and target OS support it. The cross-NAT variant uses a temporary reverse SSH path for signaling only and requires a selected srflx or prflx media pair; media never travels through SSH. The one-link media variant instead carries the same signaling through the App's temporary public origin and requires direct media delivery to an independent Linux peer. Native P2P quality evidence and embedded SFU delivery have explicit gates. macOS and Linux capture still require physical desktop/media gates; CI compilation and package smoke do not substitute for them.