| 696 | |
| 697 | // Probe size safely via API. Prefer HEAD; if missing Content-Length, fall back to a 1-byte Range GET. |
| 698 | async function probeSize(url, signal, allowRange = true) { |
| 699 | try { |
| 700 | const h = await fetch(url, { method: "HEAD", credentials: "include", signal }); |
| 701 | if (h.ok) { |
| 702 | const len = h.headers.get("content-length") ?? h.headers.get("Content-Length"); |
| 703 | if (len && !Number.isNaN(parseInt(len, 10))) return parseInt(len, 10); |
| 704 | } |
| 705 | } catch (e) { } |
| 706 | if (!allowRange) return null; |
| 707 | try { |
| 708 | const r = await fetch(url, { |
| 709 | method: "GET", |
| 710 | headers: { Range: "bytes=0-0" }, |
| 711 | credentials: "include", |
| 712 | signal |
| 713 | }); |
| 714 | // Content-Range: bytes 0-0/12345 |
| 715 | const cr = r.headers.get("content-range") ?? r.headers.get("Content-Range"); |
| 716 | const m = cr && cr.match(/\/(\d+)\s*$/); |
| 717 | if (m) return parseInt(m[1], 10); |
| 718 | } catch (e) { } |
| 719 | return null; |
| 720 | } |
| 721 | |
| 722 | const sizeProbe = (sizeHintBytes !== null) |
| 723 | ? Promise.resolve(sizeHintBytes) |