OverviewFilesCommitsIssuesDocsReleasesPeers

wirecore / public / trystero-torrent.min.js

1 lines · javascript ref:
const{floor:e,random:r}=Math,t="Trystero",n=(e,r)=>Array(e).fill().map(r),a="0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz",o=t=>n(t,(()=>a[e(62*r())])).join(""),s=o(20),i=Promise.all.bind(Promise),c="undefined"!=typeof window,{entries:l,fromEntries:d,keys:u}=Object,f=()=>{},y=e=>Error(`${t}: ${e}`),p=new TextEncoder,m=new TextDecoder,w=e=>p.encode(e),g=e=>m.decode(e),h=(...e)=>e.join("@"),b=JSON.stringify,k=JSON.parse,v={};let P=null,T=null;const A=()=>{P||(P=new Promise((e=>{T=e})).finally((()=>{T=null,P=null})))},S=()=>T?.(),L="AES-GCM",D={},I=async e=>D[e]||=Array.from(await(async(e,r)=>new Uint8Array(await crypto.subtle.digest(e,w(r))))("SHA-1",e)).map((e=>e.toString(36))).join(""),$=async(e,r)=>{const t=crypto.getRandomValues(new Uint8Array(16));return t.join(",")+"$"+(n=await crypto.subtle.encrypt({name:L,iv:t},await e,w(r)),btoa(String.fromCharCode.apply(null,new Uint8Array(n))));var n},E=async(e,r)=>{const[t,n]=r.split("$");return g(await crypto.subtle.decrypt({name:L,iv:new Uint8Array(t.split(","))},await e,(e=>{const r=atob(e);return new Uint8Array(r.length).map(((e,t)=>r.charCodeAt(t))).buffer})(n)))},C="icegatheringstatechange",U="offer";var _=(e,{rtcConfig:r,rtcPolyfill:t,turnConfig:n})=>{const a=new(t||RTCPeerConnection)({iceServers:O.concat(n||[]),...r}),o={};let s=!1,c=!1,l=null;const d=e=>{e.binaryType="arraybuffer",e.bufferedAmountLowThreshold=65535,e.onmessage=e=>o.data?.(e.data),e.onopen=()=>o.connect?.(),e.onclose=()=>o.close?.(),e.onerror=e=>o.error?.(e)},u=e=>Promise.race([new Promise((r=>{const t=()=>{"complete"===e.iceGatheringState&&(e.removeEventListener(C,t),r())};e.addEventListener(C,t),t()})),new Promise((e=>setTimeout(e,5e3)))]).then((()=>({type:e.localDescription.type,sdp:e.localDescription.sdp.replace(/a=ice-options:trickle\s\n/g,"")})));return e?(l=a.createDataChannel("data"),d(l)):a.ondatachannel=({channel:e})=>{l=e,d(e)},a.onnegotiationneeded=async()=>{try{s=!0,await a.setLocalDescription();const e=await u(a);o.signal?.(e)}catch(e){o.error?.(e)}finally{s=!1}},a.onconnectionstatechange=()=>{["disconnected","failed","closed"].includes(a.connectionState)&&o.close?.()},a.ontrack=e=>{o.track?.(e.track,e.streams[0]),o.stream?.(e.streams[0])},a.onremovestream=e=>o.stream?.(e.stream),e&&(a.canTrickleIceCandidates||a.onnegotiationneeded()),{created:Date.now(),connection:a,get channel(){return l},get isDead(){return"closed"===a.connectionState},async signal(r){if("open"!==l?.readyState||r.sdp?.includes("a=rtpmap"))try{if(r.type===U){if(s||"stable"!==a.signalingState&&!c){if(e)return;await i([a.setLocalDescription({type:"rollback"}),a.setRemoteDescription(r)])}else await a.setRemoteDescription(r);await a.setLocalDescription();const t=await u(a);return o.signal?.(t),t}if("answer"===r.type){c=!0;try{await a.setRemoteDescription(r)}finally{c=!1}}}catch(e){o.error?.(e)}},sendData(e){return l.send(e)},destroy(){l?.close(),a.close(),s=!1,c=!1},setHandlers(e){return Object.assign(o,e)},offerPromise:e?new Promise((e=>o.signal=r=>{r.type===U&&e(r)})):Promise.resolve(),addStream(e){return e.getTracks().forEach((r=>a.addTrack(r,e)))},removeStream(e){return a.getSenders().filter((r=>e.getTracks().includes(r.track))).forEach((e=>a.removeTrack(e)))},addTrack(e,r){return a.addTrack(e,r)},removeTrack(e){const r=a.getSenders().find((r=>r.track===e));r&&a.removeTrack(r)},replaceTrack(e,r){const t=a.getSenders().find((r=>r.track===e));if(t)return t.replaceTrack(r)}}};const O=[...n(3,((e,r)=>`stun:stun${r||""}.l.google.com:19302`)),"stun:stun.cloudflare.com:3478"].map((e=>({urls:e}))),j=Object.getPrototypeOf(Uint8Array),H=16369,R=255,J="bufferedamountlow",M=e=>"@_"+e;var x=(e,r,a)=>{const o={},s={},p={},m={},h={},v={},P={},T={onPeerJoin:f,onPeerLeave:f,onPeerStream:f,onPeerTrack:f},A=(e,r)=>(e?Array.isArray(e)?e:[e]:u(o)).flatMap((e=>{const n=o[e];return n?r(e,n):(console.warn(`${t}: no peer with id ${e} found`),[])})),S=e=>{o[e]&&(o[e].destroy(),delete o[e],delete m[e],delete h[e],T.onPeerLeave(e),r(e))},L=e=>{if(s[e])return p[e];if(!e)throw y("action type argument is required");const r=w(e);if(r.byteLength>12)throw y(`action type string "${e}" (${r.byteLength}b) exceeds byte limit (12). Hint: choose a shorter name.`);const t=new Uint8Array(12);t.set(r);let a=0;return s[e]={onComplete:f,onProgress:f,setOnComplete(r){return s[e]={...s[e],onComplete:r}},setOnProgress(r){return s[e]={...s[e],onProgress:r}},async send(e,r,s,c){if(s&&"object"!=typeof s)throw y("action meta argument must be an object");const l=typeof e;if("undefined"===l)throw y("action data cannot be undefined");const d="string"!==l,u=e instanceof Blob,f=u||e instanceof ArrayBuffer||e instanceof j;if(s&&!f)throw y("action meta argument can only be used with binary data");const p=f?new Uint8Array(u?await e.arrayBuffer():e):w(d?b(e):e),m=s?w(b(s)):null,g=Math.ceil(p.byteLength/H)+(s?1:0)||1,h=n(g,((e,r)=>{const n=r===g-1,o=s&&0===r,i=new Uint8Array(15+(o?m.byteLength:n?p.byteLength-H*(g-(s?2:1)):H));return i.set(t),i.set([a],12),i.set([n|o<<1|f<<2|d<<3],13),i.set([Math.round((r+1)/g*R)],14),i.set(s?o?m:p.subarray((r-1)*H,r*H):p.subarray(r*H,(r+1)*H),15),i}));return a=a+1&R,i(A(r,(async(e,r)=>{const{channel:t}=r;let n=0;for(;n<g;){const a=h[n];if(t.bufferedAmount>t.bufferedAmountLowThreshold&&await new Promise((e=>{const r=()=>{t.removeEventListener(J,r),e()};t.addEventListener(J,r)})),!o[e])break;r.sendData(a),n++,c?.(a[14]/R,e,s)}})))}},p[e]||=[s[e].send,s[e].setOnComplete,s[e].setOnProgress]},D=async()=>{await G(""),await new Promise((e=>setTimeout(e,99))),l(o).forEach((([e,r])=>{r.destroy(),delete o[e]})),a()},[I,$]=L(M("ping")),[E,C]=L(M("pong")),[U,_]=L(M("signal")),[O,x]=L(M("stream")),[q,B]=L(M("track")),[G,N]=L(M("leave"));return e(((e,r)=>{o[r]||(o[r]=e,e.setHandlers({data(e){return((e,r)=>{const n=new Uint8Array(r),a=g(n.subarray(0,12)).replaceAll("\0",""),[o]=n.subarray(12,13),[i]=n.subarray(13,14),[c]=n.subarray(14,15),l=n.subarray(15),d=!!(1&i),u=!!(2&i),f=!!(4&i),y=!!(8&i);if(!s[a])return void console.warn(`${t}: received message with unregistered type (${a})`);m[e]||={},m[e][a]||={};const p=m[e][a][o]||={chunks:[]};if(u?p.meta=k(g(l)):p.chunks.push(l),s[a].onProgress(c/R,e,p.meta),!d)return;const w=new Uint8Array(p.chunks.reduce(((e,r)=>e+r.byteLength),0));if(p.chunks.reduce(((e,r)=>(w.set(r,e),e+r.byteLength)),0),delete m[e][a][o],f)s[a].onComplete(w,e,p.meta);else{const r=g(w);s[a].onComplete(y?k(r):r,e)}})(r,e)},stream(e){T.onPeerStream(e,r,v[r]),delete v[r]},track(e,t){T.onPeerTrack(e,t,r,P[r]),delete P[r]},signal(e){return U(e,r)},close(){return S(r)},error(e){console.error(e),S(r)}}),T.onPeerJoin(r))})),$(((e,r)=>E("",r))),C(((e,r)=>{h[r]?.(),delete h[r]})),_(((e,r)=>o[r]?.signal(e))),x(((e,r)=>v[r]=e)),B(((e,r)=>P[r]=e)),N(((e,r)=>S(r))),c&&addEventListener("beforeunload",D),{makeAction:L,leave:D,async ping(e){if(!e)throw y("ping() must be called with target peer ID");const r=Date.now();return I("",e),await new Promise((r=>h[e]=r)),Date.now()-r},getPeers(){return d(l(o).map((([e,r])=>[e,r.connection])))},addStream(e,r,t){return A(r,(async(r,n)=>{t&&await O(t,r),n.addStream(e)}))},removeStream(e,r){return A(r,((r,t)=>t.removeStream(e)))},addTrack(e,r,t,n){return A(t,(async(t,a)=>{n&&await q(n,t),a.addTrack(e,r)}))},removeTrack(e,r){return A(r,((r,t)=>t.removeTrack(e)))},replaceTrack(e,r,t,n){return A(t,(async(t,a)=>{n&&await q(n,t),a.replaceTrack(e,r)}))},onPeerJoin(e){return T.onPeerJoin=e},onPeerLeave(e){return T.onPeerLeave=e},onPeerStream(e){return T.onPeerStream=e},onPeerTrack(e){return T.onPeerTrack=e}}};const q={},B={},G={},N={},z={},K={},V={},W={},F=async e=>{if(B[e])return B[e];const r=(await I(e)).slice(0,20);return B[e]=r,G[r]=e,r},Q=async(e,r,t)=>e.send(b({action:"announce",info_hash:await F(r),peer_id:s,...t})),X=(e,r,n)=>console.warn(`${t}: torrent tracker ${n?"failure":"warning"} from ${e} - ${r}`),Y=(({init:e,subscribe:r,announce:a})=>{const o={};let l,d,u,p,m=!1;return(g,v,P)=>{const{appId:T}=g;if(o[T]?.[v])return o[T][v];const D={},C={},U=h(t,T,v),O=I(U),j=I(h(U,s)),H=(async(e,r,t)=>crypto.subtle.importKey("raw",await crypto.subtle.digest({name:"SHA-256"},w(`${e}:${r}:${t}`)),{name:L},!1,["encrypt","decrypt"]))(g.password||"",T,v),R=e=>async r=>({type:r.type,sdp:await e(H,r.sdp)}),J=R(E),M=R($),q=()=>_(!0,g),B=(e,r,t)=>{C[r]?C[r]!==e&&e.destroy():(C[r]=e,Q(e,r),D[r]?.forEach(((e,r)=>{r!==t&&e.destroy()})),delete D[r])},G=(e,r)=>{C[r]===e&&delete C[r]},N=e=>(d.push(...n(e,q)),i(d.splice(0,e).map((e=>e.offerPromise.then(M).then((r=>({peer:e,offer:r}))))))),z=(e,r)=>P?.({error:`incorrect password (${g.password}) when decrypting ${r}`,appId:T,peerId:e,roomId:v}),K=e=>async(r,t,n)=>{const[a,o]=await i([O,j]);if(r!==a&&r!==o)return;const{peerId:c,offer:l,answer:d,peer:u}="string"==typeof t?k(t):t;if(c!==s&&!C[c])if(!c||l||d){if(l){const r=D[c]?.[e];if(r&&s>c)return;const t=_(!1,g);let a;t.setHandlers({connect(){return B(t,c,e)},close(){return G(t,c)}});try{a=await J(l)}catch{return void z(c,"offer")}if(t.isDead)return;const[o,d]=await i([I(h(U,c)),t.signal(a)]);n(o,b({peerId:s,answer:await M(d)}))}else if(d){let r;try{r=await J(d)}catch(e){return void z(c,"answer")}if(u)u.setHandlers({connect(){return B(u,c,e)},close(){return G(u,c)}}),u.signal(r);else{const t=D[c]?.[e];t&&!t.isDead&&t.signal(r)}}}else{if(D[c]?.[e])return;const[[{peer:r,offer:t}],a]=await i([N(1),I(h(U,c))]);D[c]||=[],D[c][e]=r,setTimeout((()=>((e,r)=>{if(C[e])return;const t=D[e]?.[r];t&&(delete D[e][r],t.destroy())})(c,e)),.9*V[e]),r.setHandlers({connect(){return B(r,c,e)},close(){return G(r,c)}}),n(a,b({peerId:s,offer:t}))}};if(!g)throw y("requires a config map as the first argument");if(!T&&!g.firebaseApp)throw y("config map is missing appId field");if(!v)throw y("roomId argument required");if(!m){const r=e(g);d=n(20,q),l=Array.isArray(r)?r:[r],m=!0,u=setInterval((()=>d=d.filter((e=>{const r=Date.now()-e.created<57333;return r||e.destroy(),r}))),59052.99),p=g.manualRelayReconnection?f:(()=>{if(c){const e=new AbortController;return addEventListener("online",S,{signal:e.signal}),addEventListener("offline",A,{signal:e.signal}),()=>e.abort()}return f})()}const V=l.map((()=>5333)),W=[],F=l.map((async(e,t)=>r(await e,await O,await j,K(t),N)));i([O,j]).then((([e,r])=>{const t=async(n,o)=>{const s=await a(n,e,r);"number"==typeof s&&(V[o]=s),W[o]=setTimeout((()=>t(n,o)),V[o])};F.forEach((async(e,r)=>{await e,t(await l[r],r)}))}));let Q=f;return o[T]||={},o[T][v]=x((e=>Q=e),(e=>delete C[e]),(()=>{delete o[T][v],W.forEach(clearTimeout),F.forEach((async e=>(await e)())),clearInterval(u),p(),m=!1}))}})({init(e){return((e,r,t)=>(e.relayUrls||r).slice(0,e.relayUrls?e.relayUrls.length:e.relayRedundancy||t))(e,re,3).map((e=>{const r=((e,r)=>{const t={},n=()=>{const a=new WebSocket(e);a.onclose=()=>{P?P.then(n):(v[e]??=3333,setTimeout(n,v[e]),v[e]*=2)},a.onmessage=e=>r(e.data),t.socket=a,t.url=a.url,t.ready=new Promise((r=>a.onopen=()=>{r(t),v[e]=3333})),t.send=e=>{1===a.readyState&&a.send(e)}};return n(),t})(e,(e=>{const r=k(e),n=r["failure reason"],a=r["warning message"],{interval:o}=r,s=G[r.info_hash];if(n)X(t,n,!0);else{if(a&&X(t,a),o&&1e3*o>K[t]&&z[t][s]){const e=Math.min(1e3*o,120333);clearInterval(N[t][s]),K[t]=e,N[t][s]=setInterval(z[t][s],e)}V[r.offer_id]||(r.offer||r.answer)&&(V[r.offer_id]=!0,W[t][s]?.(r))}})),{url:t}=r;return q[t]=r,W[t]={},r.ready}))},subscribe(e,r,t,n,a){const{url:s}=e,i=async()=>{const t=d((await a(10)).map((e=>[o(20),e])));W[e.url][r]=a=>{if(a.offer)n(r,{offer:a.offer,peerId:a.peer_id},((t,n)=>Q(e,r,{answer:k(n).answer,offer_id:a.offer_id,to_peer_id:a.peer_id})));else if(a.answer){const e=t[a.offer_id];e&&n(r,{answer:a.answer,peerId:a.peer_id,peer:e.peer})}},Q(e,r,{numwant:10,offers:l(t).map((([e,{offer:r}])=>({offer_id:e,offer:r})))})};return K[s]=33333,z[s]||={},z[s][r]=i,N[s]||={},N[s][r]=setInterval(i,K[s]),i(),()=>{clearInterval(N[s][r]),delete W[s][r],delete z[s][r]}},announce(e){return K[e.url]}}),Z=(ee=q,()=>d(l(ee).map((([e,r])=>[e,r.socket]))));var ee;const re=["tracker.webtorrent.dev","tracker.openwebtorrent.com","tracker.btorrent.xyz","tracker.files.fm:7073/announce"].map((e=>"wss://"+e));export{re as defaultRelayUrls,Z as getRelaySockets,Y as joinRoom,A as pauseRelayReconnection,S as resumeRelayReconnection,s as selfId};