codexradar.com/cup Submission
Submitting a single-file HTML work to https://codexradar.com/cup/ end to end: the upload modal, the playback-check gate, the confirmation, and the read-back that proves the entry is really public. This skill is the site-specific mapping plus a runnable driver — the generic multi-stage form mechanics live in browser-form-automation, and how the work itself is authored is out of scope.
When to Use
- Submitting or re-submitting a work to https://codexradar.com/cup/
- Checking whether an entry is public/listed, or how much capacity is left
- Don't use for: generic form automation (use
browser-form-automation) or authoring the work itself.
Prerequisites
terminal(command="bash scripts/start_chrome.sh")must printcdp up on 9222— it starts headless Chromium with CDP and polls the port (DevToolsActivePort appears late; launching in the foreground blocks and returns exit -1).- python3 with
websocketsandrequests. - A work file: one HTML file, UTF-8, smaller than
maxBytesfrom/api/cup/config(1048576 = 1 MiB).
Site rules (verbatim — they constrain the file you upload)
> 请保留完整 HTML 中的 SVG、CSS 和内联动画脚本。同一文件重复提交会自动关联。服务器只检查格式、结构、编码、大小和复杂度,不执行作者代码。
> 预览在独立、无同源沙箱中运行;网络请求、外链资源、子框架、插件、表单、弹窗、下载、顶层跳转和动态代码执行会被隔离策略拦截。
So the upload must be self-contained: no external resources, no iframes/forms/popups/downloads, no top-level navigation, no eval/new Function/fetch/XMLHttpRequest. The server does not execute the code for its checks — a passing format check is not proof the work animates, so verify the preview yourself.
Endpoints
| URL | Gives you |
|---|---|
GET /api/cup/config |
playOrigin, maxBytes, submission{limit,accepted,closed,closedAt}, voting flags |
GET /api/cup/work/<id> |
id, hash, track, metadata{title,model}, hidden, views/votes, playUrl, thumbUrl, downloadUrl |
GET /cup/play/<hash> |
public player; embeds /cup/raw/<hash>?fit=1 in a sandboxed iframe |
GET /cup/raw/<hash>?fit=1 |
the served work = your file plus an appended player fit script |
Procedure
- Start the browser, then read the config:
curl -s https://codexradar.com/api/cup/config | python3 -m json.tool. Completion criterion:closed:falseand your file is undermaxBytes. - Run
FILE=... TITLE=... MODEL=... TRACK=open python3 templates/submit_flow.py preview(the constants at the top of the template are the fallback if you prefer editing the file). It opens the dialog, fills the two text inputs and the track select, attaches the file over CDP, clicks上传并预览, and polls the dialog. Completion criterion: STEP3 echoes<name> <bytes>and the state reaches预览运行正常,请确认动画效果— or, for an already-submitted file,已上传过(see Behaviour worth knowing). - Review the preview screenshot it saves with
vision_analyze. The pane is cross-origin by policy, so a screenshot is the only way to see the work inside the sandbox. Completion criterion: the work renders correctly, 16:9, with no error text in the dialog. - Re-run as
python3 templates/submit_flow.py submit— it ticks the confirmation checkbox and presses确认提交(irreversible). Completion criterion: the receipt dialog reports作品已提交with a 作品编号. - Verify independently (next section). Completion criterion: API record, gallery listing and player page all check out.
- Hand the receipt PNG to the user and keep the 私密领奖码 out of the transcript.
Field map
- open button:
我要参赛→ dialogJOIN THE JAM(there is no class/id wrapper to target — scope every query withdocument.querySelector('[role=dialog],dialog,.modal')) - stage 1 header
JOIN THE JAM / 上传作品; stage 2 headerCONFIRM / 确认播放 - text input 0 = 作品名称 (title), input 1 = 模型/推理档位 — state what actually produced the work; never invent a tier
select= 参赛赛道:open(Open · 自由创作、多轮打磨) orclassicinput[type=file]— hidden behind a styled drop zone, still settable上传并预览→正在检查播放…→ ready state预览运行正常,请确认动画效果- checkbox
我已预览,动画播放正常,确认提交参赛; final buttons确认提交/返回修改资料 - receipt:
作品已提交,公开作品编号,私密领奖码,保存凭证 PNG - Wait on the right signal. The checkbox label is already in the DOM while the check is still running (observed in one run:
正在检查播放…and the checkbox present at the same moment), and the dialog's own step instructions contain the string确认提交. Gate on我已预览present and正在检查/上传中absent; anything looser fires before the check finishes and silently reports success.
Technique notes
- Text inputs need the native value setter plus
inputandchangeevents — a plain.value =assignment is discarded by React:
Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set.call(el,v). - Filter inputs by the
.typeproperty;input[type=text]misses inputs with no explicittypeattribute. - Set the file input over CDP (
DOM.getDocument→DOM.querySelector→DOM.setFileInputFiles), never from JS; then read backfiles[0].nameand.size. - Checkbox:
.click(), then assert.checked === truebefore pressing submit. - One flow = one script run. Every
cdp.Tab(url)opens a new tab and re-navigation resets the React dialog — the title, the attached file and the finished preview all vanish. - Keep CJK literal (UTF-8) in the script.
\uXXXXescapes are a typo factory: title鹈鹕written as\u9e48\u9e49shipped as鹈鹉, a wrong code point that only shows up when someone reads the listing.
Verify it landed (a success toast is not proof)
curl -s -A "Mozilla/5.0" "https://codexradar.com/api/cup/work/<id>" | python3 -m json.tool
Assert hidden:false plus track, metadata.title, metadata.model. Then:
- load the
playUrl(or/cup/raw/<hash>?fit=1) in a tab, wait ~5 s, screenshot, and review: renders at 16:9, not stretched or cropped, no error page; - diff the served raw file against your local file — it should be your bytes plus appended lines (the player's fit script), which is exactly why sizes and hashes differ;
- type the work-ID prefix into the gallery search box (
寻找作品编号或名称) and confirm the card appears (marked 我的作品 in the submitting browser).
Behaviour worth knowing
- Re-submitting the same file auto-links to the existing entry; a different file creates a new entry. Confirm with the user before spending a second entry.
- Same-file re-uploads are visible in the dialog itself: it shows
这份作品已上传过。查看原作品and skips the playback check, so the ready state never appears. The driver detects that branch and reports it rather than timing out. Previewing costs nothing (预览不占名额,以正式提交成功为准) — only 确认提交 registers anything. - The preview iframe is cross-origin on purpose — there is no handle to read it, so verify by screenshot and by the API/public page instead of probing for one.
- Gallery counts and badges refresh on their own schedule; the API record and the gallery search are the authoritative checks.
- Fill in
MODELhonestly. If the harness/model is unknown, ask rather than guessing.
Pitfalls
- Chromium launched in the foreground blocks and returns exit -1 — background it and poll
http://127.0.0.1:9222/json/version. - Heredocs containing
&, quotes or CJK get rejected by the command guard — write the driver to a file and run it. cdp.Tabtakes now/h; size the viewport viatab.shot(path, w=, h=).- Screenshot filenames that collide across runs make you review a stale image — the template prefixes a timestamp.
- A modal that "disappears" after a click means you navigated or the open step re-ran: re-open it inside the same script.
- Never echo the claim code; deliver the receipt image to the user only.
Verification
bash scripts/start_chrome.shprintscdp up on 9222./api/cup/configshowsclosed:falseand the file is undermaxBytes.- The receipt shows a 作品编号 and
/api/cup/work/<id>reads backhidden:falsewith the expected title/model/track. - The player page screenshots clean at 16:9 and the gallery search finds the ID.
- The claim code appears nowhere in the transcript; the user is given the work ID, the play URL and the receipt image.