@vkontakte/videoplayer-statistics 1.0.119-dev.ebda080eb.0 → 1.0.120-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/evergreen.esm.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @vkontakte/videoplayer-statistics v1.0.119-dev.ebda080eb.0
3
- * Wed, 09 Sep 2026 10:38:10 GMT
4
- * https://st.mycdn.me/static/vkontakte-videoplayer/1-0-119/doc/
2
+ * @vkontakte/videoplayer-statistics v1.0.120-beta.0
3
+ * Thu, 17 Sep 2026 07:13:59 GMT
4
+ * https://st.mycdn.me/static/vkontakte-videoplayer/1-0-120/doc/
5
5
  */
6
- var e=Object.defineProperty,t=e=>e;function n(e,n){this[e]=t.bind(null,n)}var r=(t,r)=>{for(var i in r)e(t,i,{get:r[i],enumerable:!0,configurable:!0,set:n.bind(r,i)})},i=[`adv`],a;(e=>{e.PROD=`prod`,e.VK_ALIAS=`vk_alias`,e.VIDEOTEST=`videotest`,e.TEST=`test`,e.OKCDN=`okcdn`,e.AUTO=`auto`})(a||={});var o;(e=>{e.Q144P=`mobile`,e.Q240P=`lowest`,e.Q360P=`low`,e.Q480P=`medium`,e.Q720P=`high`,e.Q1080P=`fullhd`,e.Q1440P=`quadhd`,e.Q2160P=`ultrahd`,e.UNKNOWN=`unknown`})(o||={});var s;(e=>{e.MP4=`mp4`,e.DASH=`dash`,e.DASH_SEP=`dash_sep`,e.ONDEMAND_DASH=`ondemand_dash`,e.HLS=`hls`,e.HLS_FMP4=`hls_fmp4`,e.ONDEMAND_HLS=`ondemand_hls`,e.WEBM=`webm`,e.AV1=`av1`,e.ONDEMAND_DASH_LIVE=`ondemand_dash_live`,e.ONDEMAND_HLS_LIVE=`ondemand_hls_live`,e.WEBRTC=`webrtc`,e.UNKNOWN=`unknown`,e.RTMP=`rtmp`})(s||={});var c;(e=>{e.HTTP1=`http1`,e.HTTP2=`http2`,e.HTTP3=`http3`})(c||={});var l;(e=>{e.PIP=`pip`,e.FULLSCREEN=`fullscreen`,e.EXTERNAL=`external`,e.PREFETCH=`prefetch`,e.AIRPLAY=`airplay`,e.CHROMECAST=`chromecast`,e.INVISIBLE=`invisible`,e.MINIMAL=`minimal`,e.SCREENCAST=`screencast`})(l||={});var u;(e=>{e.ADV=`adv`,e.COMMON=`common`})(u||={});var d;(e=>{e.SLIDER=`slider`,e.RICH=`rich`,e.DOUBLE_TAP=`double_tap`,e.TIME_CODE=`time_code`,e.EPISODE=`episode`,e.REWIND=`rewind`,e.LIVE=`live`,e.UNKNOWN=`unknown`})(d||={});var f;(e=>{e.GRAPH_SHOW=`iGraphShow`,e.GRAPH_HIDE=`iGraphHide`,e.NEXT_AREA=`iNextChapterArea`,e.NEXT_BUTTON=`iNextChapterBtn`,e.WATCH_AGAIN=`iWatchAgainBtn`})(f||={});var p={action_play:`ap`,action_play_interactive:`api`,first_bytes:`fb`,player_ready:`pr`,first_frame:`ff`,seek:`sk`,watch_coverage_record:`wr`,watch_coverage_live:`wl`,empty_buffer:`eb`,action_stop:`as`,close_at_empty_buffer:`cb`,content_error:`er`,player_interface_click:`pc`,next_movie:`nm`,track_switch:`ts`,failover:`fo`,quality:`qt`},m={vsid:`si`,isid:`is`,vid:`vi`,place:`pl`,quality:`qt`,cdn_host:`ch`,stat_type:`st`,param:`pm`,vk_app_id:`va`,track_code:`tc`,connection_type:`cnt`,connection_reused:`cr`,cached_data:`cd`,live:`lv`,muted:`mu`,mode:`mo`,subtitles:`sb`,failover:`fo`,download_speed:`ds`,manual_quality:`mq`,ref_domain:`rd`,direct_url:`du`},h={unknown:`un`,mobile:`m`,lowest:`ls`,low:`l`,medium:`md`,high:`h`,fullhd:`f`,quadhd:`q`,ultrahd:`u`},g={pip:`pi`,fullscreen:`fs`,external:`ex`,prefetch:`pr`,airplay:`ap`,chromecast:`cc`,invisible:`iv`,minimal:`minimal`,screencast:`screencast`};import{fillWithDefault as _}from"@vkontakte/videoplayer-shared/evergreen";var v=512000749287,y=`CIOPGQJGDIHBABABA`,b={prod:`https://api.ok.ru`,vk_alias:`https://api.mycdn.me`,videotest:`https://videotestapi.ok.ru/api`,test:`https://apitest.ok.ru`,okcdn:`https://api.okcdn.ru`,auto:``},ee={apiEnv:`vk_alias`,apiKey:y,apiApplicationId:v,apiBaseUrl:null,apiUvRestBaseUrl:`https://uvapi.okcdn.ru`,apiCollector:`ok.mobile.apps.video`,apiForticom:!0,apiUvRest:!1,requestRetryCount:1,useVsid64:!1,firstFlushTime:5e3,flushDebounceTime:1e4,flushMaxWait:6e4,storageExpiration:2160*60*1e3,watchCoverageInterval:15e3,disabledOperations:[`quality`],disabledCustomFields:[],shorten:!0,maxLoops:100,embedUrlParams:[],useBeacon:!0,synchronizeTime:!0,debugLog:!1,clearStorageAtUnload:!0,useVKComIsMobileLogic:!1,backoff:{start:1e3,factor:1.5,max:300*1e3,random:.1},useKeepalive:!1,watchCoverageTimeoutFix:!0,watchCoverageExactTime:!1,useStallContextForCloseAtEmptyBuffer:!0},te=e=>_(e,ee);function x(){let e=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`.split(``),t=Array(36),n=0,r,i;for(i=0;i<36;i++)i===8||i===13||i===18||i===23?t[i]=`-`:i===14?t[i]=`4`:(n<=2&&(n=33554432+Math.random()*16777216|0),r=n&15,n>>=4,t[i]=e[i===19?r&3|8:r]);return t.join(``)}import{assertNonNullable as ne,combine as re,fromEvent as ie,isNonNullable as S,isNullable as ae,merge as oe,now as C,observableFrom as se,once as ce,safeStorage as le,Subject as ue,Subscription as de,ValueSubject as w,getWindow as fe}from"@vkontakte/videoplayer-shared/evergreen";import{Logger as pe,detectEmbed as me}from"@vkontakte/videoplayer-shared/evergreen";var he=()=>Math.floor(Math.random()*2**32).toString(36).padStart(13,`0`),ge=()=>Math.floor(Math.random()*2**64).toString(36).padStart(13,`0`);class T{static vsid;static getVSID(){return T.vsid}static generateVSID(e){return T.vsid=e?ge():he(),T.vsid}}var _e=e=>{let{operation:t,custom:n}=e,r=Object.fromEntries(Object.entries(n).map(([e,t])=>{let n=m[e]??e,r=t;return t&&(e===`mode`?r=g[t]??t:e===`quality`&&(r=h[t]??t)),[n,r]}));return{...e,operation:p[t]??t,custom:r}};import{getWindow as ve}from"@vkontakte/videoplayer-shared/evergreen";var ye=async e=>{let t=ve().crypto?.subtle;if(!t)return;let n=new TextEncoder().encode(e),r=await t.digest(`SHA-256`,n);return Array.from(new Uint8Array(r)).map(e=>e.toString(16).padStart(2,`0`)).join(``)},E=`1.0.119-dev.ebda080eb.0`;import{ValueSubject as be,getExponentialDelay as xe,ErrorCategory as D,getWindow as Se}from"@vkontakte/videoplayer-shared/evergreen";import{createTimers as Ce,TimerModule as we}from"@vkontakte/videoplayer-shared/evergreen";var O=Ce(we.STATISTICS),Te=/Mobile|mini|Fennec|Android|iP(ad|od|hone)|opera (mini|mobi)/i;class Ee{id=`forticom`;params;authToken;sessionKey;authorizePromise;refreshAuthTokenPromise;consequentAuthErrors=0;authorized$=new be(!1);backoffTimeoutId;constructor(e){this.params=e}async authorize(e){return this.authToken=e??await this.refreshAuthToken(),this._authorizeWithBackoff()}logBeacon(e){let t=this.createLogParams(e),n=this.sessionKey;n&&(this.params.apiTransport.sendBeacon(`log.externalLog`,t,n)||this.logRequest(e).catch(()=>{}))}async logRequest(e){let t=`log.externalLog`,n=this.createLogParams(e),r=this.sessionKey??await this._authorizeWithBackoff();if(!r)return;let i=async(e,a=this.params.config.requestRetryCount)=>{try{return await this.params.apiTransport.sendRequest(t,n,r)}catch(n){if(!n||!(`error_code`in n)){this.params.error$.next({id:`logRequestUnknown`,category:D.NETWORK,message:`Unknown ${t} error`,thrown:n});return}let r=n?.error_code;switch(r){case 102:case 103:case 104:return this.authorized$.next(!1),this.sessionKey=await this._authorizeWithBackoff(),a>0?i(e,a-1):void 0;case 401:return this.authorized$.next(!1),this.authToken=await this.refreshAuthToken(),this.sessionKey=await this._authorizeWithBackoff(),a>0?i(e,a-1):void 0;default:this.params.error$.next({id:`LogRequest#${r}`,category:D.EXTERNAL_API,message:`${t} error`,data:n});return}}};return i(e)}destroy(){O.clearTimeout(this.backoffTimeoutId),this.backoffTimeoutId=0}async refreshAuthToken(){if(this.params.refreshAuthToken)return this.refreshAuthTokenPromise||=this.params.refreshAuthToken().finally(()=>{this.refreshAuthTokenPromise=void 0}),this.refreshAuthTokenPromise}createLogParams(e){let t=`WEB`,n=!1,r=`@vkontakte/videoplayer-statistics:${E}`;if(this.params.config.useVKComIsMobileLogic){let{appVersion:e}=Se().navigator;n=Te.test(e)}else this.params.mobile&&(n=!0);return n&&(t=`M_WEB`),this.params.vktvVersion&&(t=`SMART_TV`,r=this.params.vktvVersion),{collector:this.params.config.apiCollector,data:JSON.stringify({application:r,platform:t,items:this.params.config.shorten?e.map(_e):e})}}_authorizeWithBackoff(){if(!this.consequentAuthErrors)return this._authorize();let e=xe(this.consequentAuthErrors,this.params.config.backoff);return new Promise(t=>{this.backoffTimeoutId||=O.setTimeout(()=>{this._authorize().then(t).catch(e=>this.params.error$.next({id:`AuthorizeBackoff`,category:D.NETWORK,message:`Otherwise unhandled error in authorization`,thrown:e})).finally(()=>this.backoffTimeoutId=0)},e)})}async _authorize(){if(this.authorizePromise)return this.authorizePromise;this.sessionKey=void 0,this.authorized$.next(!1);let e={session_data:{version:2,device_id:this.params.deviceId,client_version:E.split(`-`)[0],client_type:`SDK_JS`}};return this.authToken&&(e.session_data.auth_token=this.authToken,e.session_data.version=3),this.authorizePromise=this.params.apiTransport.sendRequest(`auth.anonymLogin`,e).then(e=>((!e||!e.session_key)&&this.params.error$.next({id:`AuthorizeFailed`,category:D.EXTERNAL_API,message:`No session key`,data:e}),this.sessionKey=e?.session_key??void 0,this.sessionKey)).catch(async e=>{this.sessionKey=void 0;let t=e?.error_code;switch(t){case 401:return this.authToken=await this.refreshAuthToken(),this._authorizeWithBackoff()}t?this.params.error$.next({id:`Authorize#${t}`,category:D.EXTERNAL_API,message:`authorize error`,data:e}):this.params.error$.next({id:`AuthorizeUnknown`,category:D.NETWORK,message:`authorize error`,thrown:e})}).finally(()=>{this.authorizePromise=void 0,this.consequentAuthErrors=this.sessionKey?0:this.consequentAuthErrors+1,this.authorized$.next(this.sessionKey!==void 0)}),this.authorizePromise}}class De{id=`uvrest`;authorized$=new be(!1);params;statToken;authorizePromise;isDestroyed=!1;constructor(e){this.params=e}async authorize(){if(!this.isDestroyed)return this.authorizePromise?this.authorizePromise:(this.authorized$.next(!1),this.authorizePromise=this.executeAuthorize().finally(()=>{this.authorizePromise=void 0,this.isDestroyed||this.authorized$.next(this.statToken!==void 0)}),this.authorizePromise)}async logRequest(e){if(!e.length)return;let t=this.buildBody(e);try{let e=await this.getValidToken();if(!e){this.params.error$.next({id:`OneStat:UvRestApi:LogRequestNoToken`,category:D.NETWORK,message:`No stat token available for log request`});return}await this.params.apiTransport.sendJsonRequest(`/uv/stat/externalLog`,t,{Authorization:`Bearer ${e}`})}catch(e){await this.handleLogRequestError(e)&&await this.retryLogRequestAfterRecovery(t)}}logBeacon(e){if(!e.length)return;let t=this.statToken;if(!t)return;let n=this.buildBody(e);this.params.apiTransport.sendJsonBeacon(`/uv/stat/externalLog`,n,{Authorization:`Bearer ${t}`})}destroy(){this.isDestroyed=!0,this.authorizePromise=void 0,this.statToken=void 0,this.authorized$.next(!1)}async getValidToken(){return this.statToken?this.statToken:this.authorize()}async executeAuthorize(){try{let e=this.params.getAuthToken?await this.params.getAuthToken():await this.selfAuthorize();if(this.isDestroyed)return;if(!e){this.params.error$.next({id:`OneStat:UvRestApi:NoToken`,category:D.EXTERNAL_API,message:`No stat token obtained`});return}return this.statToken=e,this.statToken}catch(e){if(this.isDestroyed)return;this.params.error$.next({id:`OneStat:UvRestApi:AuthorizeFailed`,category:D.NETWORK,message:`Failed to get stat token`,thrown:e});return}}async selfAuthorize(){let e=this.params.timeSynchronisation?.now()??Date.now(),t=this.params.userId===void 0?``:String(this.params.userId),n=this.params.config.apiApplicationId,r=this.params.deviceId,i=await ye(`${t}${n}${r}${e}`);if(!i){this.params.error$.next({id:`OneStat:UvRestApi:SignatureUnavailable`,category:D.WTF,message:`crypto.subtle is unavailable for self authorization`});return}return(await this.params.apiTransport.sendJsonRequest(`/uv/stat/token`,{userId:t,applicationId:n,deviceId:r},{"X-Signature-Timestamp":String(e),Signature:i}))?.token}async handleLogRequestError(e){let t=e?.status;return t===401?(this.params.error$.next({id:`OneStat:UvRestApi:TokenExpired`,category:D.EXTERNAL_API,message:`Stat token expired (401), refreshing`,thrown:e}),this.authorized$.next(!1),this.statToken=void 0,await this.authorize()!==void 0):(this.params.error$.next({id:`OneStat:UvRestApi:LogRequestFailed#${t??`unknown`}`,category:D.EXTERNAL_API,message:`Log request failed with status ${t}`,thrown:e}),!1)}async retryLogRequestAfterRecovery(e){let t=this.statToken;if(!t){this.params.error$.next({id:`OneStat:UvRestApi:RetryNoToken`,category:D.NETWORK,message:`Cannot retry log request: no token after recovery`});return}try{await this.params.apiTransport.sendJsonRequest(`/uv/stat/externalLog`,e,{Authorization:`Bearer ${t}`})}catch(e){this.params.error$.next({id:`OneStat:UvRestApi:RetryFailed`,category:D.EXTERNAL_API,message:`Log request failed after token refresh`,thrown:e})}}buildBody(e){let t=`WEB`,n=!1,r=`@vkontakte/videoplayer-statistics:${E}`;if(this.params.config.useVKComIsMobileLogic){let{appVersion:e}=Se().navigator;n=Te.test(e)}else this.params.mobile&&(n=!0);return n&&(t=`M_WEB`),this.params.vktvVersion&&(t=`SMART_TV`,r=this.params.vktvVersion),{application:r,platform:t,user_id:this.params.userId,items:this.params.config.shorten?e.map(_e):e}}}import{ErrorCategory as k,now as A,getWindow as j}from"@vkontakte/videoplayer-shared/evergreen";class Oe{apiKey;apiBaseUrl;apiUvRestBaseUrl;apiEnv;timeSynchronisation;isApiBaseUrlFetched;params;constructor(e){this.params=e,this.apiKey=e.config.apiKey,this.apiEnv=e.config.apiEnv,this.apiBaseUrl=e.config.apiBaseUrl??b[this.apiEnv],this.apiUvRestBaseUrl=e.config.apiUvRestBaseUrl,this.timeSynchronisation=e.timeSynchronisation,this.isApiBaseUrlFetched=!1}async resolveApiBaseUrl(){if(this.apiEnv!==`auto`||this.isApiBaseUrlFetched)return this.apiBaseUrl;try{let e=atob(`aHR0cHM6Ly9kbnMuZ29vZ2xlL3Jlc29sdmU/bmFtZT12aWRlby5fZW5kcG9pbnQub2sucnUmdHlwZT1UWFQ=`),t=(await(await fetch(e,{method:`GET`,mode:`cors`,cache:`no-cache`})).json())?.Answer[0]?.data;if(!t)throw Error(`Wrong DNS response`);return t}catch(e){return this.params.error$.next({id:`OneStat:ApiTransport:resolveApiBaseUrl`,category:k.NETWORK,message:`Unhandled resolve api base url error`,thrown:e}),b.vk_alias}finally{this.isApiBaseUrlFetched=!0}}sendBeacon(e,t,n){if(!j().Blob||!j().navigator.sendBeacon)return!1;let r=this._prepareQueryParams({method:e,queryParams:t,sessionKey:n}),i=j().Blob,a=new i([r.toString()],{type:`application/x-www-form-urlencoded`});try{return j().navigator.sendBeacon(`${this.apiBaseUrl}/fb.do`,a)}catch(n){this.params.error$.next({id:`OneStat:ApiTransport:sendBeacon`,category:k.NETWORK,message:`Unhandled beacon error`,thrown:n,data:{method:e,params:t}})}return!1}async sendRequest(e,t,n){let r=A(),i=i=>{if(i instanceof TypeError&&[`Failed to fetch`,`NetworkError when attempting to fetch resource.`].includes(i.message)){this.params.error$.next({id:`Network`,category:k.NETWORK,message:`Request failed`,thrown:i});return}this.params.error$.next({id:`OneStat:ApiTransport:sendRequest`,category:k.NETWORK,message:`Unhandled request error`,thrown:i,data:{method:e,params:t,sessionKey:n,time:A()-r}})};this.apiBaseUrl=await this.resolveApiBaseUrl();let a={method:`post`,headers:{"Content-type":`application/x-www-form-urlencoded`},body:this._prepareQueryParams({method:e,queryParams:t,sessionKey:n})};return this.params.config.useKeepalive&&(a.keepalive=!0),fetch(`${this.apiBaseUrl}/fb.do`,a).then(e=>{let t=Number(e.headers.get(`content-length`))===0,n=new Date(e.headers.get(`date`)??``).getTime(),a=A()-r;if(isFinite(n)&&this.timeSynchronisation?.addServerTime(n,a),!t)return e.json().then(e=>Object.prototype.hasOwnProperty.call(e,`error_msg`)?Promise.reject(e):e,i)},i)}sendJsonBeacon(e,t,n){let r=`${this.apiUvRestBaseUrl}${e}`;try{fetch(r,{method:`POST`,headers:{"Content-Type":`application/json`,...n},body:JSON.stringify(t),keepalive:!0}).catch(t=>{this.params.error$.next({id:`OneStat:ApiTransport:sendJsonBeacon`,category:k.NETWORK,message:`Keepalive request failed`,thrown:t,data:{path:e}})})}catch(t){this.params.error$.next({id:`OneStat:ApiTransport:sendJsonBeacon`,category:k.NETWORK,message:`Failed to initiate keepalive request`,thrown:t,data:{path:e}})}}async sendJsonRequest(e,t,n){let r=A(),i=t=>{if(t instanceof TypeError&&[`Failed to fetch`,`NetworkError when attempting to fetch resource.`].includes(t.message)){this.params.error$.next({id:`Network`,category:k.NETWORK,message:`JSON request failed`,thrown:t});return}this.params.error$.next({id:`OneStat:ApiTransport:sendJsonRequest`,category:k.NETWORK,message:`Unhandled JSON request error`,thrown:t,data:{path:e,time:A()-r}})},a={method:`POST`,headers:{"Content-Type":`application/json`,...n},body:JSON.stringify(t)};return this.params.config.useKeepalive&&(a.keepalive=!0),fetch(`${this.apiUvRestBaseUrl}${e}`,a).then(e=>{let t=Number(e.headers.get(`content-length`))===0,n=new Date(e.headers.get(`date`)??``).getTime(),a=A()-r;if(isFinite(n)&&this.timeSynchronisation?.addServerTime(n,a),!e.ok)return Promise.reject({status:e.status,statusText:e.statusText});if(!t)return e.json().catch(i)},i)}_prepareQueryParams(e){let t=new URLSearchParams({format:`JSON`,method:e.method,application_key:this.apiKey});return e.sessionKey!==void 0&&t.append(`session_key`,e.sessionKey),Object.entries(e.queryParams).forEach(([e,n])=>t.append(e,typeof n==`string`?n:JSON.stringify(n))),t}}import{ErrorCategory as ke,now as Ae,safeStorage as M,debounceFn as je}from"@vkontakte/videoplayer-shared/evergreen";var Me=e=>t=>t.timestamp+e>=Ae();class Ne{params;error$;userSalt;isPaused=!1;loggerDebugLog;queues=[];constructor(e){this.params=e,this.error$=e.error$,this.userSalt=e.userSalt,this.loggerDebugLog=e.debugLogger.createComponentLog(`stat logger`);for(let t of e.apis)this.queues.push(this.createQueueState(t));this.housekeepStorage()}createQueueState(e){let t=`onestat_events_${e.id}`,n=je(()=>this.safeFlushQueue(a),this.params.config.firstFlushTime,{maxWait:this.params.config.firstFlushTime}),r=M.isPersistent()?this.params.config.flushMaxWait:0,i=je(()=>this.safeFlushQueue(a),this.params.config.flushDebounceTime,{maxWait:r}),a={api:e,storageKey:t,unsaltedStorage:[],lastVsid:void 0,firstFlush:n,debouncedFlush:i,subscription:e.authorized$.subscribe(e=>{e&&i()})};return a}isUrgent=e=>{let{operation:t}=e;return[`action_play`,`action_play_interactive`,`watch_coverage_record`,`watch_coverage_live`,`action_stop`,`close_at_empty_buffer`].includes(t)};safeFlushQueue(e){try{this.flushQueue(e)}catch(t){this.error$.next({id:`LoggerError`,category:ke.WTF,message:t?String(t):`Unknown logger error`,thrown:t,data:{storageKey:e.storageKey}})}}readFromStorage(e){let t=M.get(e);try{return t?JSON.parse(t):{}}catch{}return{}}addToStorage(e,t){if(!this.userSalt){e.unsaltedStorage.push(t);return}let n=this.readFromStorage(e.storageKey),r=(n[this.userSalt]??[]).filter(Me(this.params.config.storageExpiration));M.set(e.storageKey,JSON.stringify({...n,[this.userSalt]:[...r,t]}))}getFromStorage(e){return this.userSalt?(this.readFromStorage(e.storageKey)[this.userSalt]??[]).filter(Me(this.params.config.storageExpiration)):e.unsaltedStorage}markStorageSent(e){if(!this.userSalt){e.unsaltedStorage=[];return}let t=this.readFromStorage(e.storageKey);delete t[this.userSalt],M.set(e.storageKey,JSON.stringify(t))}housekeepStorage(){for(let e of this.queues){let t=this.readFromStorage(e.storageKey);for(let[n,r]of Object.entries(t)){let i=r.filter(Me(this.params.config.storageExpiration));i.length?t[n]=i:delete t[n],this.loggerDebugLog({message:`${r.length} retrieved from storage, ${i.length} of them actual (${e.storageKey})`})}M.set(e.storageKey,JSON.stringify(t))}}log(e){for(let t of this.queues)this.addToStorage(t,e),!this.isPaused&&(this.isUrgent(e)?this.flushQueue(t):t.lastVsid===e.custom.vsid?t.debouncedFlush():t.firstFlush(),t.lastVsid=e.custom.vsid)}flush(e={wantBeacon:!1,clearStorage:!0}){for(let t of this.queues)this.flushQueue(t,e)}flushQueue(e,{wantBeacon:t,clearStorage:n}={wantBeacon:!1,clearStorage:!0}){let r=this.getFromStorage(e);r.length!==0&&(e.api.authorized$.getValue()?(t&&this.params.config.useBeacon?(this.loggerDebugLog({message:`Flushing ${r.length} events through beacon (${e.storageKey})`}),e.api.logBeacon(r)):(this.loggerDebugLog({message:`Flushing ${r.length} events (${e.storageKey})`}),e.api.logRequest(r)),n&&this.markStorageSent(e)):e.api.authorize(),e.firstFlush.cancel(),e.debouncedFlush.cancel())}pause(){this.isPaused=!0;for(let e of this.queues)e.debouncedFlush.cancel()}resume(){this.isPaused=!1;for(let e of this.queues)e.debouncedFlush()}destroy(){for(let e of this.queues)e.subscription.unsubscribe(),e.firstFlush.cancel(),e.debouncedFlush.cancel();this.queues=[]}}import{HttpConnectionType as Pe,Surface as N,VideoFormat as P,VideoQuality as F}from"@vkontakte/videoplayer-core/evergreen";import{assertNever as Fe}from"@vkontakte/videoplayer-shared/evergreen";var Ie=e=>e&&{[F.INVARIANT]:`unknown`,[F.Q_144P]:`mobile`,[F.Q_240P]:`lowest`,[F.Q_360P]:`low`,[F.Q_480P]:`medium`,[F.Q_576P]:`unknown`,[F.Q_720P]:`high`,[F.Q_1080P]:`fullhd`,[F.Q_1440P]:`quadhd`,[F.Q_2160P]:`ultrahd`,[F.Q_4320P]:`unknown`}[e],Le=e=>e&&{[Pe.HTTP1]:`http1`,[Pe.HTTP2]:`http2`,[Pe.QUIC]:`http3`}[e],Re=e=>{if(e!==void 0)switch(e){case P.MPEG:return`mp4`;case P.DASH:case P.DASH_LIVE:case P.DASH_STREAMS:return`dash`;case P.DASH_SEP:return`dash_sep`;case P.DASH_ONDEMAND:return`ondemand_dash`;case P.DASH_WEBM:case P.DASH_LIVE_WEBM:return`webm`;case P.DASH_WEBM_AV1:return`av1`;case P.DASH_LIVE_CMAF:return`ondemand_dash_live`;case P.HLS:case P.HLS_LIVE:return`hls`;case P.HLS_FMP4:return`hls_fmp4`;case P.HLS_ONDEMAND:return`ondemand_hls`;case P.HLS_LIVE_CMAF:return`ondemand_hls_live`;case P.WEB_RTC_LIVE:return`webrtc`;default:return Fe(e)}},ze=(e,t)=>{if(t)return`minimal`;switch(e){case void 0:case N.NONE:case N.INLINE:return;case N.FULLSCREEN:return`fullscreen`;case N.SECOND_SCREEN:return`chromecast`;case N.PIP:return`pip`;case N.INVISIBLE:return`invisible`;default:return Fe(e)}},Be=e=>{switch(e){case`slow-2g`:return`poor`;case`2g`:return`poor`;case`3g`:return`good`;case`4g`:return`excellent`}};class Ve{offset=void 0;constructor(e){this.offset=e}getOffset(){return this.offset??0}setOffset(e){this.offset=e}now(){return Date.now()+(this.offset??0)}date(e=new Date){return e.setTime(e.getTime()+(this.offset??0)),e}addServerTime(e,t){let n=e-Date.now()-t/2;if(Math.abs(n)<1e3){this.offset=0;return}if(this.offset===void 0)this.offset=Math.round(n);else{let e=.2;this.offset=Math.round(e*n+(1-e)*this.offset)}}}import{PlaybackState as He}from"@vkontakte/videoplayer-core/evergreen";import{filter as Ue,filterChanged as We,fromEvent as Ge,isNullable as Ke,map as qe,merge as I,Observable as Je,Subject as Ye,Subscription as Xe,ValueSubject as Ze}from"@vkontakte/videoplayer-shared/evergreen";var Qe=(e,t)=>new Je(n=>{let r=new Xe,i=I(Ge(window,`beforeunload`),e.events.willDestruct$),a=new Ze(void 0),o;r.add(e.info.isLive$.pipe(We()).subscribe(t=>{o&&(o.unsubscribe(),a.next(void 0)),o=t?e.info.liveTime$.pipe(qe(e=>e&&e/1e3)).subscribe(a):e.info.position$.subscribe(a),r.add(o)}));let{playing$:s,paused$:c,looped$:l}=e.events,u=e.events.willSeek$.pipe(Ue(()=>e.info.playbackState$.getValue()===He.PLAYING)),d=e.events.seeked$.pipe(Ue(()=>e.info.playbackState$.getValue()===He.PLAYING)),f=!1,p=new Ye;r.add(u.subscribe(()=>{f||p.next(),f=!0})),r.add(d.subscribe(()=>f=!1));let m=new Ye,h=new Ye,g=()=>t.watchCoverageExactTime?e.info.isLive$.getValue()?e.getExactLiveTime():e.getExactTime():a.getValue()??0,_=e.info.stallStartTime$.pipe(We(),Ue(e=>e>0)),v=I(s,d,m).pipe(qe(()=>g())),y=I(l,I(c,p,i,h,_).pipe(qe(()=>g()))),b,ee=e=>{b=e},te=e=>{let t=e;Ke(b)||b===t||Ke(t)||(n.next({from:b,to:t}),b=void 0)},x=()=>{h.next(),m.next()};if(r.add(v.subscribe(ee)),r.add(y.subscribe(te)),t.forceInterval&&isFinite(t.forceInterval))if(t.watchCoverageTimeoutFix){let e,n=()=>{e!==void 0&&(O.clearTimeout(e),e=void 0)};r.add(v.subscribe(()=>{n(),e=O.setTimeout(x,t.forceInterval)})),r.add(y.subscribe(()=>n())),r.add(()=>n())}else{let e=0;r.add(v.subscribe(()=>e=O.setTimeout(x,t.forceInterval))),r.add(y.subscribe(()=>O.clearTimeout(e)))}return r}),$e=new Set(i),et=e=>$e.has(e);import{Surface as L}from"@vkontakte/videoplayer-core/evergreen";import{debounce as tt,filterChanged as nt,Subject as rt}from"@vkontakte/videoplayer-shared/evergreen";var it=({uiEvents:e,initExternalSubscribers:t,subscribe:n,player:r})=>{let i=new rt;t(i.pipe(tt(0),nt()));let a=!1,o=!1,s=!1;n(e.inPiP$,e=>{a=e,i.next(e?L.PIP:o?L.FULLSCREEN:s?L.INLINE:void 0)}),n(e.inFullscreen$,e=>{o=e,i.next(e?L.FULLSCREEN:a?L.PIP:s?L.INLINE:void 0)}),r&&n(r.info.surface$,e=>{switch(e){case L.SECOND_SCREEN:case L.INVISIBLE:case L.NONE:case L.INLINE:!o&&!a&&(e===L.INLINE&&(s=!0),i.next(e));break}})},at=`_one-stat_deviceId`,ot=(e,t)=>e&&t?`${e}_${t}`:e??t,st=({width:e,height:t})=>e&&t?`${Math.round(e)}x${Math.round(t)}`:void 0,ct=()=>{let e=new de;return{subscription:e,subscribe:(t,n)=>{t&&e.add(t.subscribe(n))}}};class lt{apis;logger;config;subscription=new de;beforeunloadSubscription;timeSynchronisation;debugLogger=new pe;oneStatDebugLog=this.debugLogger.createComponentLog(`onestat`);loopCounter=0;disabled=!1;sessionMode=`common`;surfaceMode;experimental;get vsid$(){return new w(T.getVSID())}isid$=new w(void 0);zenUid$=new w(void 0);statContext;isEmbed;embedParent;uiEvents;player;seekAction$=new w(`unknown`);deviceId;playerSize={width:0,height:0};constructor(e,t){this.statContext=e,this.config=te(t.config??{}),S(t.apiEnv)&&(this.config.apiEnv=t.apiEnv),this.config.synchronizeTime&&(this.timeSynchronisation=new Ve);let n=new ue;this.experimental={error$:n};let r=le.get(at);this.statContext.deviceId?this.deviceId=this.statContext.deviceId:r?this.deviceId=r:(this.deviceId=x(),le.set(at,this.deviceId)),this.resetViewSession(),t.useIsid&&this.isid$.next(t.isid??ge()),this.zenUid$.next(t.zenUid);let i=new Oe({config:this.config,error$:n,timeSynchronisation:this.timeSynchronisation}),a=[];this.config.apiForticom&&a.push(new Ee({config:this.config,apiTransport:i,refreshAuthToken:t.refreshAuthToken,mobile:this.statContext.mobile??!1,deviceId:this.deviceId,error$:n,vktvVersion:t.vktvVersion})),this.config.apiUvRest&&a.push(new De({config:this.config,apiTransport:i,error$:n,userId:this.statContext.userId,deviceId:this.deviceId,getAuthToken:t.getUvRestAuthToken,mobile:this.statContext.mobile??!1,vktvVersion:t.vktvVersion,timeSynchronisation:this.timeSynchronisation})),this.apis=a,this.logger=new Ne({config:this.config,debugLogger:this.debugLogger,apis:this.apis,error$:n,userSalt:t.userSalt});let{isEmbed:o,topOrigin:s}=me();this.isEmbed=o,this.embedParent=s?new URL(s).hostname:void 0,this.subscribe()}updateContext(e){this.statContext={...this.statContext,...e}}attachTo(e){let t=new de,n=(e,n)=>t.add(e.subscribe(n)),r,i,a,o=!1,s=!1,c=()=>{o||(o=!0,S(r)&&(r={startedAt:C(),liveAtStall:!!this.statContext.liveEdge}))};n(e.events.willStart$,()=>{let t=e.info.position$.getValue();this.logActionPlay({position:t}),this.statContext.projectId&&this.logActionPlayInteractive()}),n(e.events.looped$,()=>{this.resetViewSession(),++this.loopCounter>this.config.maxLoops&&(this.disabled=!0),this.logActionPlay({position:e.info.position$.getValue()})}),n(e.events.willSeek$,()=>{S(r)&&o&&this.logEmptyBuffer({duration:C()-r.startedAt,liveAtStall:r.liveAtStall}),r=void 0,s=!0}),n(e.events.seeked$,()=>{!s&&S(r)&&o&&(this.logEmptyBuffer({duration:C()-r.startedAt,liveAtStall:r.liveAtStall}),r=void 0),s=!1;let t=this.seekAction$.getValue();this.logSeek({action:t===`rich`?`slider`:t,time:e.info.position$.getValue()}),this.seekAction$.next(`unknown`)}),n(e.events.paused$,()=>{this.logPause({position:e.info.position$.getValue()})}),n(e.events.willResume$,()=>{this.logPlay()}),n(e.events.started$,()=>{this.statContext.clip&&this.logWatchCoverageRecord({start:0,end:0})}),n(e.events.playing$,()=>{this.logPlaying()}),n(Qe(e,{forceInterval:this.config.watchCoverageInterval,watchCoverageTimeoutFix:this.config.watchCoverageTimeoutFix,watchCoverageExactTime:this.config.watchCoverageExactTime}),t=>{let n=e.info.isLive$.getValue(),r=e.info.atLiveEdge$.getValue(),i={start:parseFloat(t.from.toFixed(3)),end:parseFloat(t.to.toFixed(3))};if(r){let t=e.info.liveLatency$.getValue()??0,n=e.info.currentVideoBuffer$.getValue()?.end??0,r=e.info.liveBufferTime$.getValue()??0,a=Math.round((t-(n-r))*1e3),o=Math.round(t*1e3);a&&o&&(i.latency=a,i.bufferLatency=o)}n?this.logWatchCoverageLive(i):this.logWatchCoverageRecord(i)}),n(e.info.stallStartTime$,e=>{e>0?r={startedAt:e,liveAtStall:!!this.statContext.liveEdge}:(S(r)&&o&&this.logEmptyBuffer({duration:Math.round(C()-r.startedAt),liveAtStall:r.liveAtStall}),r=void 0)});let l=!1;t.add(e.events.fatalError$.pipe(ce()).subscribe(()=>l=!0)),n(e.events.willStop$,()=>{(this.config.useStallContextForCloseAtEmptyBuffer?S(r)&&o:e.info.isBuffering$.getValue()&&o)?(this.logCloseAtEmptyBuffer({duration:S(r)?Math.round(C()-r.startedAt):0,liveAtStall:r?.liveAtStall}),r=void 0):l||this.logActionStop()}),n(e.events.managedError$,({id:e})=>{this.logError({fatal:!1,errorType:e})}),n(e.events.fatalError$,({id:e})=>{this.logError({fatal:!0,errorType:e})}),n(e.events.firstBytes$,e=>{i=C(),this.logFirstBytes({time:e})}),n(e.events.willStart$,()=>a=C()),n(e.info.currentVideoBuffer$,e=>{!o&&e&&e.end-e.start>0&&S(i)&&(this.logPlayerReady({duration:C()-i}),c())}),n(e.events.firstFrame$,()=>{S(i)&&!o&&(this.logPlayerReady({duration:C()-i}),c()),S(a)&&this.logFirstFrame({time:C()-a})});let u;n(e.info.currentVideoStream$,e=>{e&&(this.updateContext({videoCodec:e.codec}),u&&e.id!==u&&this.logTrackSwitch(e),u=e.id)});let d;n(e.info.currentAudioStream$,e=>{e&&(this.updateContext({audioCodec:e.codec}),d&&e.id!==d&&this.logTrackSwitch(e),d=e.id)}),n(e.info.atLiveEdge$,e=>this.updateContext({liveEdge:e})),n(re({muted:e.info.muted$,volume:e.info.volume$}),({muted:e,volume:t})=>this.updateContext({audible:!e&&t>0})),n(e.info.currentQuality$,e=>{let t=Ie(e);this.updateContext({quality:t}),t&&this.logQuality(t)}),n(e.info.isAutoQualityEnabled$,e=>this.updateContext({autoQuality:e})),n(e.info.currentFormat$,e=>this.updateContext({contentType:Re(e)})),n(e.info.currentPlaybackRate$,e=>this.updateContext({rate:e}));let f;return n(e.info.hostname$,e=>{let t=f!==void 0&&f!==e;this.updateContext({cdnHostname:e,failover:t}),t&&this.logFailover(e),f=e}),n(e.info.throughputEstimation$,e=>this.updateContext({downloadSpeed:e})),n(e.info.httpConnectionType$,e=>{this.statContext.firstConnectionType||this.updateContext({firstConnectionType:Le(e)}),this.updateContext({connectionType:Le(e)})}),n(e.info.httpConnectionReused$,e=>{ae(this.statContext.firstConnectionReused)&&this.updateContext({firstConnectionReused:e}),this.updateContext({connectionReused:e})}),n(e.info.surface$,e=>{this.uiEvents||(this.surfaceMode=ze(e,this.statContext.isPreviewPlayerView))}),n(re({current:e.info.currentTextTrack$,available:e.info.availableTextTracks$}),({current:e,available:t})=>{let n=t.find(({id:t})=>t===e),r=n&&(n.isAuto?`${n.language}_auto`:n.language);this.updateContext({subtitles:r?.split(`.`)[0]})}),this.player=e,this.uiEvents&&this.attachToUi(this.uiEvents),this.resubscribeBeforeunload(),this.subscription.add(t),t}attachToUi(e){this.uiEvents=e;let{subscription:t,subscribe:n}=ct();return this.player?(n(e.actionRewind$,()=>{this.player&&(this.resetViewSession(),this.logActionPlay({position:this.player.info.position$.getValue()}))}),n(e.actionSeek$,this.seekAction$),it({uiEvents:this.uiEvents,initExternalSubscribers:e=>{n(e,e=>{this.surfaceMode=ze(e,this.statContext.isPreviewPlayerView)})},subscribe:n,player:this.player}),n(e.actionSetSubtitle$,e=>this.updateContext({subtitles:e?.split(`.`)[0]})),n(e.nextMovie$,e=>this.logNextMovie(e)),n(e.playerSize$,({width:e,height:t})=>{e&&t&&(this.playerSize={width:e,height:t})}),n(e.fatalError$,e=>e&&this.logError({fatal:!0,errorType:e.type})),this.resubscribeBeforeunload(),this.subscription.add(t),t):t}attachToAds(e){let{subscription:t,subscribe:n}=ct();return n(e.slotRequested$,()=>this.logAdSlotRequest()),n(e.started$,e=>this.logAdStarted(e)),n(e.paused$,()=>this.logAdPaused()),n(e.resumed$,()=>this.logAdResumed()),n(e.ended$,()=>this.logAdEnded()),n(e.skipped$,()=>this.logAdSkipped()),n(e.clicked$,()=>this.logAdClicked()),n(e.error$,e=>this.logError({errorType:e,fatal:!1})),this.resubscribeBeforeunload(),this.subscription.add(t),t}attachToInteractive(e){let{subscription:t,subscribe:n}=ct();return n(e.click$,e=>this.logInterfaceClick(e)),n(e.nextMovie$,e=>this.logNextMovie(e)),this.resubscribeBeforeunload(),this.subscription.add(t),t}authorize(e){return Promise.all(this.apis.map(t=>t.authorize(e)))}setSessionMode(e){this.sessionMode=e,this.oneStatDebugLog({message:`session mode set to ${e}`})}pause(){this.logger.pause(),this.oneStatDebugLog({message:`paused`})}resume(){this.logger.resume(),this.oneStatDebugLog({message:`resumed`})}destroy(){this.logger.flush(),this.subscription.unsubscribe(),this.apis.forEach(e=>e.destroy()),this.logger.destroy()}resetViewSession(){this.oneStatDebugLog({message:`VSID reset`}),T.generateVSID(this.config.useVsid64)}getDeviceId(){return this.deviceId}setFieldBlacklist(){}logInited(){}logReady(e){this.log({operation:`player_ready`,param:String(e.time)})}logStarted(...e){this.logActionPlay(...e)}logPlay(){this.log({operation:`play_toggle`})}logPlaying(){this.log({operation:`playing`})}logPause(e){this.log({operation:`pause`,param:String(Math.round(e.position))})}logSeek(e){this.log({operation:`seek`,param:e.action,time:e.time})}logFirstBytes(e){this.log({operation:`first_bytes`,param:String(e.time)})}logFirstFrame(e){this.log({operation:`first_frame`,param:String(e.time)})}logError(e){this.log({operation:`content_error`,param:`${e.fatal?`fatal`:`recoverable`}_${e.errorType}`})}logWatchCoverageRecord(e){this.log({operation:`watch_coverage_record`,param:`${e.start}-${e.end}`},{in_history:this.statContext.inHistory?1:void 0})}logWatchCoverageLive(e){let t=this.timeSynchronisation?.getOffset()??0,n=e.start+t,r=e.end+t;this.log({operation:`watch_coverage_live`,param:`${n}-${r}`},{in_history:this.statContext.inHistory?1:void 0,latency:e.latency,buffer_latency:e.bufferLatency})}logEmptyBuffer(e){this.log({operation:`empty_buffer`,param:String(e.duration)},S(e.liveAtStall)?{live:e.liveAtStall?1:void 0}:{})}logDownloadSpeed(){}logAdSlotRequest(){this.log({operation:`adv`,param:`slot_request`})}logAdStarted(e){this.log({operation:`adv`,param:e})}logAdPaused(){this.log({operation:`adv`,param:`pause`})}logAdResumed(){this.log({operation:`adv`,param:`resume`})}logAdEnded(){this.log({operation:`adv`,param:`ended`})}logAdSkipped(){this.log({operation:`adv`,param:`skip`})}logAdClicked(){this.log({operation:`adv`,param:`click`})}logInterfaceClick(e){this.log({operation:`player_interface_click`,param:e})}logNextMovie(e){this.log({operation:`next_movie`,param:String(e)})}subscribe(){this.resubscribeBeforeunload();let e=fe().navigator.connection;e&&`onchange`in e&&`effectiveType`in e&&this.subscription.add(oe(ie(e,`change`),se([`init`])).subscribe(()=>this.updateContext({network:Be(e.effectiveType)}))),this.config.debugLog&&this.debugLogger.log$.subscribe(e=>{console.debug(`%c stat `,`background:#fa6470;`,`component: ${e.component}.`,e.message)})}resubscribeBeforeunload(){this.beforeunloadSubscription?.unsubscribe(),this.beforeunloadSubscription=ie(window,`beforeunload`).subscribe(()=>this.logger.flush({wantBeacon:!0,clearStorage:this.config.clearStorageAtUnload})),this.subscription.add(this.beforeunloadSubscription)}logPlayerReady(e){this.log({operation:`player_ready`,param:String(Math.round(e.duration))})}logActionPlay(e){this.log({operation:`action_play`,param:String(Math.round(e.position))})}logActionPlayInteractive(){this.statContext.projectId&&this.log({operation:`action_play_interactive`,param:String(this.statContext.movieId)},{vid:this.statContext.projectId})}logFailover(e){this.log({operation:`failover`,param:e})}logCloseAtEmptyBuffer(e){this.log({operation:`close_at_empty_buffer`,param:String(Math.round(e.duration))},S(e.liveAtStall)?{live:e.liveAtStall?1:void 0}:{})}logActionStop(){this.log({operation:`action_stop`})}logTrackSwitch(e){this.log({operation:`track_switch`,param:e.id})}logQuality(e){this.log({operation:`quality`,param:e})}log(e,t={}){if(this.disabled){this.oneStatDebugLog({message:`operation ${e.operation} but statistics is disabled`});return}if(this.sessionMode===`adv`&&!et(e.operation)){this.oneStatDebugLog({message:`operation ${e.operation} skipped: adv mode`});return}if(this.config.disabledOperations.includes(e.operation)){this.oneStatDebugLog({message:`operation ${e.operation} but it is disabled`});return}this.oneStatDebugLog({message:`operation ${e.operation} ${e.param}`});let n=this.createLogItem(e,t);this.logger.log(n)}createLogItem({operation:e,param:t,time:n},r={}){let i=this.timeSynchronisation?.now()??C(),a=this.vsid$.getValue();ne(a);let o=this.isid$.getValue(),s=e===`empty_buffer`||e===`close_at_empty_buffer`?this.statContext.connectionType:this.statContext.firstConnectionType,c=this.statContext.firstConnectionReused,l=this.player?.info.rttEstimation$.getValue(),u,d;if(this.isEmbed||this.statContext.place===`embed`){u=this.embedParent;let e=[...new URLSearchParams(location.search).entries()].filter(([e,t])=>this.config.embedUrlParams.includes(e));d=new URLSearchParams(e).toString()}else if(this.statContext.place===`direct`){let e=this.statContext.refDomain||document.referrer;u=e&&URL.canParse(e)?new URL(e).hostname:e,d=location.href.substring(0,1024)}let f={vsid:a,isid:o,vid:this.statContext.movieId,uid:this.zenUid$.getValue(),ct:this.statContext.contentType,place:this.isEmbed?`embed`:this.statContext.place,quality:this.statContext.quality,cdn_host:this.statContext.cdnHostname,stat_type:this.statContext.autoplay===!0?`auto`:this.statContext.autoplay===!1?``:void 0,param:t,vk_app_id:this.statContext.vkAppId,track_code:this.statContext.trackCode,connection_type:s,connection_reused:c===!0?1:c===!1?0:void 0,cached_data:this.statContext.cached===!0?1:this.statContext.cached===!1?0:void 0,live:this.statContext.liveEdge?1:void 0,muted:this.statContext.audible===!1?1:void 0,mode:this.surfaceMode??this.statContext.mode,subtitles:this.statContext.subtitles,download_speed:this.statContext.downloadSpeed,manual_quality:this.statContext.autoQuality?void 0:1,ref_domain:u,direct_url:d,rate:this.statContext.rate===1||ae(this.statContext.rate)?void 0:this.statContext.rate.toFixed(2),view_360:this.statContext.is3d?1:void 0,codec_info:ot(this.statContext.videoCodec,this.statContext.audioCodec),aid:this.statContext.albumId,vk_playlist_id:this.statContext.vkPlaylistId,vport:st(this.statContext.playerSize??this.playerSize),rtt:S(l)?Math.round(l):void 0,bw:S(this.statContext.downloadSpeed)?Math.round(this.statContext.downloadSpeed):void 0,audio_player_type:this.statContext.audioPlayerType,has_subscription:this.statContext.hasSubscription,is_downloaded:this.statContext.isDownloaded,is_url_in_cache:this.statContext.isUrlInCache,like:this.statContext.like,track_type:this.statContext.trackType,...r};for(let e of this.config.disabledCustomFields)delete f[e];return{operation:e,type:1,time:n,network:this.statContext.network,timestamp:i,custom:f}}}var ut={};r(ut,{VERSION:()=>E,ThinOneStat:()=>rr,SessionMode:()=>At,ActionSeekType:()=>jt});import{assertNonNullable as dt,detectEmbed as ft,fromEvent as pt,Logger as mt,merge as ht,combine as R,filter as z,filterChanged as gt,isNullable as _t,once as vt,skip as yt,debounce as bt,safeStorage as xt,getWindow as St,Subject as Ct,Subscription as B,ValueSubject as wt,ErrorCategory as V}from"@vkontakte/videoplayer-shared/evergreen";import{clientChecker as H}from"@vkontakte/videoplayer-core/evergreen";import{fillWithDefault as Tt}from"@vkontakte/videoplayer-shared/evergreen";var Et=[`adv_configuration`,`adv_request`,`adv_break_started`,`adv_break_ended`,`adv_error`],Dt=[`stop`,`error`,`cannot_play`],Ot=[`start_session`,`play`,`manifest_request`,`first_byte_manifest`,`first_media_request`,`first_byte_media`,`first_video_frame_decoded`,`ready`,`playback_started`,`watched_n`],kt;(e=>{e.AUTO=`auto`,e.AUTO_POOR=`auto_poor`,e.AUTO_RICH=`auto_rich`})(kt||={});var At;(e=>{e.ADV=`adv`,e.COMMON=`common`})(At||={});var jt;(e=>{e.SLIDER=`slider`,e.RICH=`rich`,e.DOUBLE_TAP=`double_tap`,e.TIME_CODE=`time_code`,e.EPISODE=`episode`,e.REWIND=`rewind`,e.LIVE=`live`,e.UNKNOWN=`unknown`})(jt||={});var Mt=512000749287,Nt=`CIOPGQJGDIHBABABA`,Pt={prod:`https://api.ok.ru`,vk_alias:`https://api.mycdn.me`,videotest:`https://videotestapi.ok.ru/api`,test:`https://apitest.ok.ru`,okcdn:`https://api.okcdn.ru`,auto:``},Ft={apiKey:Nt,apiApplicationId:Mt,apiEnv:`okcdn`,apiBaseUrl:null,apiUvRestBaseUrl:`https://uvapi.okcdn.ru`,apiForticom:!0,apiUvRest:!1,requestRetryCount:3,useVsid64:!1,flushFirstTime:5e3,flushDebounceTime:1e4,flushMaxWait:6e4,flushRetryFirstDelay:3e3,flushRetryMaxDelay:18e5,storageExpiration:2160*60*1e3,clearStorageAtUnload:!1,disabledEvents:[],disabledParams:[],synchronizeTime:!0,useBeacon:!1,alwaysSendDesktop:!1,watchCoverageHeartbeatInterval:3e4,watchCoverageExactTime:!1,watchedNDefaultTargetDuration:1e3,telemetryInterval:3e4,downloadBytesMaxTime:3e4,maxLoops:100},It=e=>Tt(e,Ft);import{ErrorCategory as U,ValueSubject as Lt}from"@vkontakte/videoplayer-shared/evergreen";function Rt(e){let t=Error(`HTTP ${e.status}: ${e.statusText}`);return t.status=e.status,t.statusText=e.statusText,t.response=e,t}function zt(){let e=Error(`Request timeout`);return e.isTimeout=!0,e}function Bt(e){let t=Error(e.error_msg);return t.errorData=e,t}function Vt(e){if(!(!e||typeof e!=`object`)){if(`errorData`in e)return e.errorData?.error_code;if(`error_code`in e){let t=e.error_code;return typeof t==`number`?t:void 0}}}var Ht=e=>e.match(/^\d+\.\d+\.\d+/)?.[0]??e,Ut=({application:e,platform:t,product:n,...r})=>r;class Wt{id=`forticom`;authorized$;params;authToken;sessionKey;authorizePromise;refreshAuthTokenPromise;isDestroyed=!1;constructor(e){this.params=e,this.authorized$=new Lt(!1)}async authorize(e){return this.authToken=e??await this.refreshAuthToken(),this.doAuthorize()}logBeacon(e){if(!e.length)return;let t=this.createLogParams(e),n=this.sessionKey;n&&(this.params.apiTransport.sendBeacon(`log.logUvStat`,t,n)||this.logRequest(e).catch(()=>{}))}async logRequest(e){if(!e.length)return;let t=`log.logUvStat`,n=this.createLogParams(e),r={};try{let e=await this.getValidSessionKey();if(!e){this.params.errors$.next({id:`ThinOneStat:Api:LogRequestNoSession`,category:U.NETWORK,message:`No session key available for log request`});return}await this.params.apiTransport.sendRequest(t,n,e,r)}catch(e){await this.handleLogRequestError(e,t,n)&&await this.retryLogRequestAfterSessionRecovery(t,n)}}async getValidSessionKey(){return this.sessionKey?this.sessionKey:this.doAuthorize()}async handleLogRequestError(e,t,n){let r=Vt(e);return r===void 0?(this.params.errors$.next({id:`ThinOneStat:Api:LogRequestUnknown`,category:U.NETWORK,message:`Unknown ${t} error`,thrown:e,data:{params:n}}),!1):this.isSessionExpiredError(r)?(this.params.errors$.next({id:`ThinOneStat:Api:LogRequestSessionError#${r}`,category:U.EXTERNAL_API,message:`Session error: ${r}`,data:{error:e,code:r}}),this.authorized$.next(!1),this.sessionKey=void 0,await this.doAuthorize()!==void 0):r===401?(this.params.errors$.next({id:`ThinOneStat:Api:LogRequestAuthLogin#${r}`,category:U.EXTERNAL_API,message:`Auth token expired`,data:{error:e,code:r}}),this.authorized$.next(!1),this.authToken=await this.refreshAuthToken(),this.sessionKey=void 0,await this.doAuthorize()!==void 0):(this.params.errors$.next({id:`ThinOneStat:Api:LogRequest#${r}`,category:U.EXTERNAL_API,message:`${t} error`,data:{error:e,code:r,params:n}}),!1)}async retryLogRequestAfterSessionRecovery(e,t){let n=this.sessionKey;if(!n){this.params.errors$.next({id:`ThinOneStat:Api:LogRequestRetryFailed`,category:U.NETWORK,message:`Cannot retry log request: no session key after recovery`});return}try{await this.params.apiTransport.sendRequest(e,t,n)}catch(e){let t=Vt(e);this.params.errors$.next({id:`ThinOneStat:Api:LogRequestRetryFailed#${t??`unknown`}`,category:U.EXTERNAL_API,message:`Log request failed after session recovery`,thrown:e,data:{originalError:e,retryCode:t}})}}isSessionExpiredError(e){return!!e&&[102,103,104].includes(e)}async doAuthorize(e=1){if(!this.isDestroyed)return this.authorizePromise?this.authorizePromise:(this.sessionKey=void 0,this.authorized$.next(!1),this.authorizePromise=this.executeAuthorize(e).finally(()=>{this.authorizePromise=void 0,this.isDestroyed||this.authorized$.next(this.sessionKey!==void 0)}),this.authorizePromise)}async executeAuthorize(e){let t=this.buildAuthParams();try{let e=await this.params.apiTransport.sendRequest(`auth.anonymLogin`,t);if(this.isDestroyed)return;if(!e?.session_key){this.params.errors$.next({id:`ThinOneStat:Api:AuthorizeFailed`,category:U.EXTERNAL_API,message:`No session key in response`,data:e});return}return this.sessionKey=e.session_key,this.sessionKey}catch(t){if(this.isDestroyed)return;let n=Vt(t);if(n===401&&this.params.refreshAuthToken){if(this.params.errors$.next({id:`ThinOneStat:Api:AuthorizeAuthLogin`,category:U.EXTERNAL_API,message:`Auth token expired during authorization`,data:{error:t,code:n,attempt:e}}),e>=2){this.params.errors$.next({id:`ThinOneStat:Api:AuthorizeMaxAttemptsReached`,category:U.EXTERNAL_API,message:`Authorization failed after ${e} attempts`,data:{error:t,code:n}});return}return this.authToken=await this.refreshAuthToken(),this.isDestroyed?void 0:this.executeAuthorize(e+1)}n?this.params.errors$.next({id:`ThinOneStat:Api:Authorize#${n}`,category:U.EXTERNAL_API,message:`Authorize error`,data:{error:t,code:n}}):this.params.errors$.next({id:`ThinOneStat:Api:AuthorizeUnknown`,category:U.NETWORK,message:`Authorize error`,thrown:t});return}}buildAuthParams(){let e={session_data:{version:2,device_id:this.params.deviceId,client_version:this.getClientVersion(),client_type:`SDK_JS`}};return this.authToken&&(e.session_data.auth_token=this.authToken,e.session_data.version=3),e}getClientVersion(){return Ht(E)}async refreshAuthToken(){if(this.params.refreshAuthToken)return this.refreshAuthTokenPromise||=this.params.refreshAuthToken().catch(e=>{throw this.params.errors$.next({id:`ThinOneStat:Api:RefreshAuthTokenFailed`,category:U.NETWORK,message:`Failed to refresh auth token`,thrown:e}),e}).finally(()=>{this.refreshAuthTokenPromise=void 0}),this.refreshAuthTokenPromise}createLogParams(e){let t=e[0],n=t.application??`@vkontakte/videoplayer-statistics:${this.getClientVersion()}`;return this.params.vktvVersion&&(n=this.params.vktvVersion),{collector:`ok.mobile.apps.video`,uv_stat_data:JSON.stringify({application:n,platform:t.platform,product:t.product,events:e.map(Ut)})}}destroy(){this.isDestroyed=!0,this.authorizePromise=void 0,this.refreshAuthTokenPromise=void 0,this.sessionKey=void 0,this.authorized$.next(!1)}}class Gt{id=`uvrest`;authorized$;params;statToken;authorizePromise;isDestroyed=!1;constructor(e){this.params=e,this.authorized$=new Lt(!1)}async authorize(e){if(!this.isDestroyed)return this.authorizePromise?this.authorizePromise:(this.authorized$.next(!1),this.authorizePromise=this.executeAuthorize().finally(()=>{this.authorizePromise=void 0,this.isDestroyed||this.authorized$.next(this.statToken!==void 0)}),this.authorizePromise)}async logRequest(e){if(!e.length)return;let t=this.buildBody(e);try{let e=await this.getValidToken();if(!e){this.params.errors$.next({id:`ThinOneStat:UvRestApi:LogRequestNoToken`,category:U.NETWORK,message:`No stat token available for log request`});return}await this.params.apiTransport.sendJsonRequest(`/uv/stat/logUvStat`,t,{Authorization:`Bearer ${e}`})}catch(e){await this.handleLogRequestError(e)&&await this.retryLogRequestAfterRecovery(t)}}logBeacon(e){if(!e.length)return;let t=this.statToken;if(!t)return;let n=this.buildBody(e);this.params.apiTransport.sendJsonBeacon(`/uv/stat/logUvStat`,n,{Authorization:`Bearer ${t}`})}destroy(){this.isDestroyed=!0,this.authorizePromise=void 0,this.statToken=void 0,this.authorized$.next(!1)}async getValidToken(){return this.statToken?this.statToken:this.authorize()}async executeAuthorize(){try{let e=this.params.getAuthToken?await this.params.getAuthToken():await this.selfAuthorize();if(this.isDestroyed)return;if(!e){this.params.errors$.next({id:`ThinOneStat:UvRestApi:NoToken`,category:U.EXTERNAL_API,message:`No stat token obtained`});return}return this.statToken=e,this.statToken}catch(e){if(this.isDestroyed)return;this.params.errors$.next({id:`ThinOneStat:UvRestApi:AuthorizeFailed`,category:U.NETWORK,message:`Failed to get stat token`,thrown:e});return}}async selfAuthorize(){let e=this.params.timeSynchronisation?.now()??Date.now(),t=this.params.userId===void 0?``:String(this.params.userId),n=this.params.config.apiApplicationId,r=this.params.deviceId,i=await ye(`${t}${n}${r}${e}`);if(!i){this.params.errors$.next({id:`ThinOneStat:UvRestApi:SignatureUnavailable`,category:U.WTF,message:`crypto.subtle is unavailable for self authorization`});return}return(await this.params.apiTransport.sendJsonRequest(`/uv/stat/token`,{userId:t,applicationId:n,deviceId:r},{"X-Signature-Timestamp":String(e),Signature:i}))?.token}async handleLogRequestError(e){let t=e?.status;return t===401?(this.params.errors$.next({id:`ThinOneStat:UvRestApi:TokenExpired`,category:U.EXTERNAL_API,message:`Stat token expired (401), refreshing`,thrown:e}),this.authorized$.next(!1),this.statToken=void 0,await this.authorize()!==void 0):(this.params.errors$.next({id:`ThinOneStat:UvRestApi:LogRequestFailed#${t??`unknown`}`,category:U.EXTERNAL_API,message:`Log request failed with status ${t}`,thrown:e}),!1)}async retryLogRequestAfterRecovery(e){let t=this.statToken;if(!t){this.params.errors$.next({id:`ThinOneStat:UvRestApi:RetryNoToken`,category:U.NETWORK,message:`Cannot retry log request: no token after recovery`});return}try{await this.params.apiTransport.sendJsonRequest(`/uv/stat/logUvStat`,e,{Authorization:`Bearer ${t}`})}catch(e){this.params.errors$.next({id:`ThinOneStat:UvRestApi:RetryFailed`,category:U.EXTERNAL_API,message:`Log request failed after token refresh`,thrown:e})}}buildBody(e){let t=e[0],n=t.application;return this.params.vktvVersion&&(n=this.params.vktvVersion),{product:t.product,platform:t.platform,application:n,user_id:this.params.userId,events:e.map(Ut)}}}import{now as W,getWindow as G,getExponentialDelay as Kt,isAbortError as qt,ErrorCategory as K}from"@vkontakte/videoplayer-shared/evergreen";class Jt{params;apiKey;apiBaseUrl;apiUvRestBaseUrl;apiEnv;resolvedApiBaseUrl=null;resolveApiBaseUrlPromise=null;timeSynchronisation;fetchTimeout=3e4;maxTotalRequestTime=6e4;retryConfig;constructor(e){this.params=e,this.apiKey=e.config.apiKey,this.apiEnv=e.config.apiEnv,this.apiBaseUrl=e.config.apiBaseUrl??Pt[this.apiEnv],this.apiUvRestBaseUrl=e.config.apiUvRestBaseUrl,this.timeSynchronisation=e.timeSynchronisation,this.retryConfig={maxAttempts:e.config.requestRetryCount??3,baseDelay:3e3,maxDelay:3e4,retryableErrors:[`Failed to fetch`,`NetworkError when attempting to fetch resource.`,`Request timeout`,`Network request failed`],retryableStatuses:[408,429,500,502,503,504]}}sendBeacon(e,t,n){if(!G().Blob||!G().navigator.sendBeacon)return!1;let r=this.resolvedApiBaseUrl??this.apiBaseUrl,i=this.prepareQueryParams({method:e,queryParams:t,sessionKey:n}),a=G().Blob,o=new a([i.toString()],{type:`application/x-www-form-urlencoded`});try{return G().navigator.sendBeacon(`${r}/fb.do`,o)}catch(n){this.params.errors$.next({id:`ThinOneStat:ApiTransport:sendBeacon`,category:K.NETWORK,message:`Unhandled beacon error`,thrown:n,data:{method:e,params:t}})}return!1}async sendRequest(e,t,n,r){return this.sendRequestWithRetry(e,t,n,r)}async sendRequestWithRetry(e,t,n,r,i=1,a){let o=W(),s=a??o+this.maxTotalRequestTime;if(W()>=s){let r=Error(`Request deadline exceeded`);throw this.handleFinalError(e,t,n,o,r,i),r}try{return await this.executeRequest(e,t,n,r,o,i,s)}catch(a){if(this.shouldRetry(a,i)&&W()<s){let c=this.getRetryDelay(a,i);if(W()+c>=s)throw this.handleFinalError(e,t,n,o,a,i),a;return this.logRetryAttempt(e,i,a,c),await this.sleep(c),this.sendRequestWithRetry(e,t,n,r,i+1,s)}throw this.handleFinalError(e,t,n,o,a,i),a}}async executeRequest(e,t,n,r,i,a,o){let s=i??W(),c=await this.resolveApiBaseUrl(),l=o===void 0?this.fetchTimeout:o-W();if(l<=0)throw zt();try{let i=await this.fetchWithTimeout(`${c}/fb.do`,{method:`post`,headers:{"Content-type":`application/x-www-form-urlencoded`,...r},body:this.prepareQueryParams({method:e,queryParams:t,sessionKey:n})},Math.min(this.fetchTimeout,l));return await this.processResponse(i,s)}catch(t){throw t instanceof Error&&(t.context={method:e,attempt:a,url:`${c}/fb.do`}),t}}async sendJsonRequest(e,t,n){return this.sendJsonRequestWithRetry(e,t,n)}sendJsonBeacon(e,t,n){let r=`${this.apiUvRestBaseUrl}${e}`;try{fetch(r,{method:`POST`,headers:{"Content-Type":`application/json`,...n},body:JSON.stringify(t),keepalive:!0}).catch(t=>{this.params.errors$.next({id:`ThinOneStat:ApiTransport:sendJsonBeacon`,category:K.NETWORK,message:`Keepalive request failed`,thrown:t,data:{path:e}})})}catch(t){this.params.errors$.next({id:`ThinOneStat:ApiTransport:sendJsonBeacon`,category:K.NETWORK,message:`Failed to initiate keepalive request`,thrown:t,data:{path:e}})}}async sendJsonRequestWithRetry(e,t,n,r=1,i){let a=W(),o=i??a+this.maxTotalRequestTime;if(W()>=o){let t=Error(`Request deadline exceeded`);throw this.handleJsonFinalError(e,a,t,r),t}try{return await this.executeJsonRequest(e,t,n,a,r,o)}catch(i){if(this.shouldRetry(i,r)&&W()<o){let s=this.getRetryDelay(i,r);if(W()+s>=o)throw this.handleJsonFinalError(e,a,i,r),i;return this.logRetryAttempt(e,r,i,s),await this.sleep(s),this.sendJsonRequestWithRetry(e,t,n,r+1,o)}throw this.handleJsonFinalError(e,a,i,r),i}}async executeJsonRequest(e,t,n,r,i,a){let o=r??W(),s=a===void 0?this.fetchTimeout:a-W();if(s<=0)throw zt();let c=`${this.apiUvRestBaseUrl}${e}`;try{let e=await this.fetchWithTimeout(c,{method:`POST`,headers:{"Content-Type":`application/json`,...n},body:JSON.stringify(t)},Math.min(this.fetchTimeout,s));return await this.processResponse(e,o)}catch(t){throw t instanceof Error&&(t.context={method:e,attempt:i,url:c}),t}}async fetchWithTimeout(e,t,n=this.fetchTimeout){let r=new AbortController,i=O.setTimeout(()=>r.abort(),n);try{let n=await fetch(e,{...t,signal:r.signal});return O.clearTimeout(i),n}catch(e){throw O.clearTimeout(i),qt(e)?zt():e}}async processResponse(e,t){if(!e.ok){let t=Rt(e),n=this.parseRetryAfter(e);throw n!==void 0&&(t.retryAfter=n),t}let n=new Date(e.headers.get(`date`)??``).getTime(),r=W()-t;isFinite(n)&&this.timeSynchronisation?.addServerTime(n,r);let i=e.headers.get(`content-length`);if(i!==null&&Number(i)===0)return{};let a=await e.json();if(a&&typeof a==`object`&&`error_msg`in a)throw Bt(a);return a}shouldRetry(e,t){if(t>=this.retryConfig.maxAttempts||!(e instanceof Error))return!1;let n=e;return!!(n.isTimeout||n.status&&this.retryConfig.retryableStatuses.includes(n.status)||this.retryConfig.retryableErrors.includes(e.message)||e.message.includes(`network`)||e.message.includes(`timeout`)||e.message.includes(`connection`))}logRetryAttempt(e,t,n,r){let i=n instanceof Error?n.message:String(n);this.params.errors$.next({id:`ThinOneStat:ApiTransport:retry`,category:K.NETWORK,message:`Retrying request (attempt ${t}/${this.retryConfig.maxAttempts}) after ${Math.round(r)}ms`,thrown:n,data:{method:e,attempt:t,delay:r,errorMessage:i}})}handleFinalError(e,t,n,r,i,a){let o=K.NETWORK;this.params.errors$.next({id:`ThinOneStat:ApiTransport:sendRequest`,category:o,message:i instanceof Error?i.message:`Request failed after retries`,thrown:i,data:{method:e,params:t,sessionKey:n,time:W()-r,maxAttempts:this.retryConfig.maxAttempts,attempts:a}})}handleJsonFinalError(e,t,n,r){this.params.errors$.next({id:`ThinOneStat:ApiTransport:sendJsonRequest`,category:K.NETWORK,message:n instanceof Error?n.message:`JSON request failed after retries`,thrown:n,data:{path:e,time:W()-t,maxAttempts:this.retryConfig.maxAttempts,attempts:r}})}sleep(e){return new Promise(t=>O.setTimeout(t,e))}resolveApiBaseUrl(){return this.apiEnv===`auto`?(this.resolveApiBaseUrlPromise||=this.fetchApiBaseUrl().then(e=>(e===Pt.vk_alias&&(this.resolveApiBaseUrlPromise=null),e)),this.resolveApiBaseUrlPromise):Promise.resolve(this.apiBaseUrl)}async fetchApiBaseUrl(){try{let e=atob(`aHR0cHM6Ly9kbnMuZ29vZ2xlL3Jlc29sdmU/bmFtZT12aWRlby5fZW5kcG9pbnQub2sucnUmdHlwZT1UWFQ=`),t=(await(await this.fetchWithTimeout(e,{method:`GET`,mode:`cors`,cache:`no-cache`},5e3)).json())?.Answer[0]?.data;if(!t)throw Error(`Wrong DNS response`);return this.resolvedApiBaseUrl=t,t}catch(e){return this.params.errors$.next({id:`ThinOneStat:ApiTransport:resolveApiBaseUrl`,category:K.NETWORK,message:`Unhandled resolve api base url error`,thrown:e}),Pt.vk_alias}}prepareQueryParams(e){let t=new URLSearchParams({format:`JSON`,method:e.method,application_key:this.apiKey});return e.sessionKey!==void 0&&t.append(`session_key`,e.sessionKey),Object.entries(e.queryParams).forEach(([e,n])=>t.append(e,typeof n==`string`?n:JSON.stringify(n))),t}parseRetryAfter(e){let t=e.headers.get(`Retry-After`);if(!t)return;let n=Number(t);if(!isNaN(n)&&n>=0)return n*1e3;let r=new Date(t).getTime();if(!isNaN(r))return Math.max(0,r-Date.now())}getRetryDelay(e,t){let n=e;return n?.retryAfter!==void 0&&n.retryAfter>0?n.retryAfter:Kt(t,{start:this.retryConfig.baseDelay,max:this.retryConfig.maxDelay,factor:2,random:.2})}}import{ErrorCategory as Yt}from"@vkontakte/videoplayer-shared/evergreen";import{now as Xt,debounceFn as Zt,getWindow as Qt,safeStorage as q,ErrorCategory as J}from"@vkontakte/videoplayer-shared/evergreen";var $t=e=>t=>(t.client_time??Xt())+e>=Xt();class en{params;subscription;userSalt;unsaltedStorage=[];lastVsid;isPaused=!1;firstFlush;debouncedFlush;flushRetryCount=0;retryFlushTimerId;debugLog;flushPromise=null;flushSnapshotLength=0;onlineListener;urgentEvents=[`watch_coverage`,`watch_coverage_live`,`watched_n`,`playback_started`];STORAGE_MAX_ATTEMPTS=3;constructor(e){this.params=e,this.userSalt=e.userSalt,this.debugLog=e.debugLogger.createComponentLog(`batch-queue:${e.storageKey}`),this.firstFlush=Zt(()=>this.safeFlush(),this.params.config.flushFirstTime,{maxWait:this.params.config.flushFirstTime});let t=q.isPersistent()?this.params.config.flushMaxWait:0;this.debouncedFlush=Zt(()=>this.safeFlush(),this.params.config.flushDebounceTime,{maxWait:t}),this.subscription=this.params.canSend$.subscribe(e=>{e&&(this.debugLog({message:`Can send, triggering flush`}),this.debouncedFlush())}),this.onlineListener=()=>{this.debugLog({message:`Network online, triggering flush`}),this.cancelRetry(),this.safeFlush()},Qt().addEventListener(`online`,this.onlineListener),this.housekeepStorage()}isUrgent=e=>this.urgentEvents.includes(e.event_name);scheduleRetry(){this.retryFlushTimerId!==void 0&&O.clearTimeout(this.retryFlushTimerId);let e=this.params.config.flushRetryFirstDelay,t=this.params.config.flushRetryMaxDelay,n=Math.min(e*2**this.flushRetryCount,t);this.flushRetryCount++,this.debugLog({message:`Scheduling flush retry #${this.flushRetryCount} in ${n}ms`}),this.retryFlushTimerId=O.setTimeout(()=>{this.retryFlushTimerId=void 0,this.safeFlush()},n)}cancelRetry(){this.retryFlushTimerId!==void 0&&(O.clearTimeout(this.retryFlushTimerId),this.retryFlushTimerId=void 0),this.flushRetryCount=0}safeFlush(){if(this.flushPromise){this.debugLog({message:`Flush already in progress, skipping`});return}this.flushPromise=this.flush().finally(()=>{this.flushPromise=null})}isStorageAvailable(){let e=q.isPersistent?.()??!1;return e||this.debugLog({message:`Storage not available`}),e}readFromStorage(){if(!this.isStorageAvailable())return{};try{let e=q.get(this.params.storageKey),t=e?JSON.parse(e):{};return this.debugLog({message:`Read ${Object.keys(t).length} users from storage`}),t}catch(e){return this.params.errors$.next({id:`ThinOneStat:BatchQueue:ReadStorageError`,category:J.WTF,message:`Failed to read from storage`,thrown:e,data:{storageKey:this.params.storageKey}}),this.debugLog({message:`Failed to read from storage`}),{}}}writeToStorage(e){if(!this.isStorageAvailable())return!1;try{return q.set(this.params.storageKey,JSON.stringify(e)),this.debugLog({message:`Written ${Object.keys(e).length} users to storage`}),!0}catch(e){return this.params.errors$.next({id:`ThinOneStat:BatchQueue:WriteStorageError`,category:J.WTF,message:`Failed to write to storage`,thrown:e,data:{storageKey:this.params.storageKey}}),this.debugLog({message:`Failed to write to storage`}),!1}}addToStorage(e){if(!this.userSalt){this.unsaltedStorage.push(e),this.debugLog({message:`Added item to unsalted storage, total: ${this.unsaltedStorage.length}`});return}for(let t=0;t<this.STORAGE_MAX_ATTEMPTS;t++)try{let t=this.readFromStorage(),n=t[this.userSalt]??[];if(this.debugLog({message:`Adding item to storage for user ${this.userSalt}`}),this.writeToStorage({...t,[this.userSalt]:[...n,e]}))return}catch(n){t===this.STORAGE_MAX_ATTEMPTS-1&&(this.params.errors$.next({id:`ThinOneStat:BatchQueue:AddToStorageFailed`,category:J.WTF,message:`Failed to add item to storage after retries`,thrown:n,data:{item:e,attempt:t,storageKey:this.params.storageKey}}),this.debugLog({message:`Failed to add item to storage after retries`}))}}getFromStorage(){if(!this.userSalt)return this.debugLog({message:`Retrieved ${this.unsaltedStorage.length} items from unsalted storage`}),this.flushSnapshotLength=this.unsaltedStorage.length,this.unsaltedStorage.slice();let e=this.readFromStorage(),t=e[this.userSalt]??[],n=t.filter($t(this.params.config.storageExpiration));if(this.debugLog({message:`Retrieved from storage for user ${this.userSalt}`}),n.length!==t.length){let r=t.length-n.length;this.params.errors$.next({id:`ThinOneStat:BatchQueue:DropExpiredEvents`,category:J.WTF,message:`Dropped ${r} expired events from storage`,data:{droppedCount:r,expiration:this.params.config.storageExpiration,storageKey:this.params.storageKey}}),this.debugLog({message:`Cleaning ${r} expired items from storage`});let i={...e};n.length?i[this.userSalt]=n:delete i[this.userSalt],this.writeToStorage(i)}return this.flushSnapshotLength=n.length,n}markStorageSent(){if(!this.userSalt){let e=this.unsaltedStorage.splice(0,this.flushSnapshotLength);this.debugLog({message:`Cleared unsalted storage, removed ${e.length} items, ${this.unsaltedStorage.length} new items kept`}),this.flushSnapshotLength=0;return}let e=this.flushSnapshotLength;this.flushSnapshotLength=0;for(let t=0;t<this.STORAGE_MAX_ATTEMPTS;t++)try{let t=this.readFromStorage(),n=(t[this.userSalt]??[]).slice(e),r={...t};if(n.length?r[this.userSalt]=n:delete r[this.userSalt],this.writeToStorage(r)){this.debugLog({message:`Marked storage as sent for user ${this.userSalt}, removed ${e} items, ${n.length} kept`});return}}catch(e){t===this.STORAGE_MAX_ATTEMPTS-1&&(this.params.errors$.next({id:`ThinOneStat:BatchQueue:MarkStorageSentFailed`,category:J.WTF,message:`Failed to mark storage as sent after retries`,thrown:e,data:{storageKey:this.params.storageKey}}),this.debugLog({message:`Failed to mark storage as sent after retries`}))}}housekeepStorage(){if(this.isStorageAvailable())try{let e=this.readFromStorage(),t=!1,n=0;for(let[r,i]of Object.entries(e)){let a=i.filter($t(this.params.config.storageExpiration)),o=i.length-a.length;n+=o,a.length?a.length!==i.length&&(e[r]=a,t=!0):(delete e[r],t=!0),this.debugLog({message:`${i.length} retrieved from storage, ${a.length} of them actual for ${r}`})}t?(this.debugLog({message:`Housekeeping completed, removed ${n} expired items total`}),this.writeToStorage(e),n>0&&this.params.errors$.next({id:`ThinOneStat:BatchQueue:DropExpiredEvents`,category:J.WTF,message:`Dropped ${n} expired events during housekeeping`,data:{droppedCount:n,expiration:this.params.config.storageExpiration,storageKey:this.params.storageKey}})):this.debugLog({message:`Housekeeping completed, no changes needed`})}catch(e){this.params.errors$.next({id:`ThinOneStat:BatchQueue:HousekeepStorageError`,category:J.WTF,message:`Failed to housekeep storage`,thrown:e,data:{storageKey:this.params.storageKey}}),this.debugLog({message:`Failed to housekeep storage`})}}log(e){try{if(this.debugLog({message:`Logging item: ${e.event_name}`}),this.addToStorage(e),this.isPaused){this.debugLog({message:`Queue paused, skipping flush`});return}this.isUrgent(e)?(this.debugLog({message:`Urgent item, flushing immediately`}),this.safeFlush()):this.lastVsid===e.vsid?(this.debugLog({message:`Same VSID, scheduling debounced flush`}),this.debouncedFlush()):(this.debugLog({message:`VSID changed, triggering first flush`}),this.firstFlush()),this.lastVsid=e.vsid}catch(t){this.params.errors$.next({id:`ThinOneStat:BatchQueue:LogError`,category:J.WTF,message:`Failed to log item`,thrown:t,data:{item:e,storageKey:this.params.storageKey}}),this.debugLog({message:`Failed to log item`})}}flush(e={wantBeacon:!1,clearStorage:!0}){return this.flushPromise?(this.debugLog({message:`Flush already in progress, waiting`}),this.flushPromise):(this.flushPromise=this.flushInternal(e).finally(()=>{this.flushPromise=null}),this.flushPromise)}async flushInternal({wantBeacon:e,clearStorage:t}){this.debugLog({message:`Starting flush`});let n=this.getFromStorage();if(n.length===0){this.debugLog({message:`No items to flush`});return}let r=this.params.canSend$.getValue();if(this.debugLog({message:`Can send status: ${r}`}),!r){this.debugLog({message:`Cannot send, requesting ready state`}),this.params.requestReady();return}if(e&&this.params.config.useBeacon){this.debugLog({message:`Flushing ${n.length} events through beacon`}),this.params.sendBeacon(n),t&&this.markStorageSent(),this.firstFlush.cancel(),this.debouncedFlush.cancel();return}this.debugLog({message:`Flushing ${n.length} events`});try{await this.params.send(n),this.debugLog({message:`Flush completed successfully`}),this.cancelRetry(),t&&this.markStorageSent(),this.firstFlush.cancel(),this.debouncedFlush.cancel()}catch(e){throw this.params.errors$.next({id:`ThinOneStat:BatchQueue:FlushRequestError`,category:J.NETWORK,message:`Failed to send events, will retry later`,thrown:e,data:{itemsCount:n.length,storageKey:this.params.storageKey}}),this.debugLog({message:`Flush failed, scheduling retry`}),this.scheduleRetry(),e}}pause(){this.debugLog({message:`Queue paused`}),this.isPaused=!0,this.debouncedFlush.cancel(),this.firstFlush.cancel(),this.cancelRetry()}resume(){this.debugLog({message:`Queue resumed`}),this.isPaused=!1,this.debouncedFlush()}async destroy(){this.debugLog({message:`Destroying queue`}),this.flushPromise&&(this.debugLog({message:`Waiting for ongoing flush to complete`}),await this.flushPromise),this.cancelRetry(),this.onlineListener&&=(Qt().removeEventListener(`online`,this.onlineListener),void 0),this.subscription.unsubscribe(),this.firstFlush.cancel(),this.debouncedFlush.cancel(),this.flushPromise=null,this.debugLog({message:`Queue destroyed`})}}class tn{params;queues=[];constructor(e){this.params=e;for(let t of e.apis)this.queues.push(this.createQueueForApi(t))}createQueueForApi(e){let t=this.getStorageKeyForApi(e.id);return new en({config:this.params.config,storageKey:t,debugLogger:this.params.debugLogger,errors$:this.params.errors$,userSalt:this.params.userSalt,send:t=>e.logRequest(t),sendBeacon:t=>e.logBeacon(t),canSend$:e.authorized$,requestReady:()=>{e.authorize().catch(e=>{this.params.errors$.next({id:`ThinOneStat:Logger:RequestReadyFailed`,category:Yt.NETWORK,message:`Failed to authorize on requestReady`,thrown:e,data:{storageKey:t}})})}})}getStorageKeyForApi(e){return`thinonestat_events_${e}`}log(e){this.queues.forEach(t=>t.log(e))}async flush(e){let t=await Promise.allSettled(this.queues.map(t=>t.flush(e)));if(t.length>0&&t.every(e=>e.status===`rejected`))throw t.find(e=>e.status===`rejected`).reason}pause(){this.queues.forEach(e=>e.pause())}resume(){this.queues.forEach(e=>e.resume())}async destroy(){await Promise.allSettled(this.queues.map(e=>e.destroy())),this.queues=[]}}var nn=new Set(Et),rn=e=>nn.has(e);function an(){let e=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`.split(``),t=Array(36),n=0,r,i;for(i=0;i<36;i++)i===8||i===13||i===18||i===23?t[i]=`-`:i===14?t[i]=`4`:(n<=2&&(n=33554432+Math.random()*16777216|0),r=n&15,n>>=4,t[i]=e[i===19?r&3|8:r]);return t.join(``)}class on{offset=void 0;constructor(e){this.offset=e}getOffset(){return this.offset??0}setOffset(e){this.offset=e}now(){return Date.now()+(this.offset??0)}date(e=new Date){return e.setTime(e.getTime()+(this.offset??0)),e}addServerTime(e,t){let n=e-Date.now()-t/2;if(Math.abs(n)<1e3){this.offset=0;return}if(this.offset===void 0)this.offset=Math.round(n);else{let e=.2;this.offset=Math.round(e*n+(1-e)*this.offset)}}}import{isNullable as sn}from"@vkontakte/videoplayer-shared/evergreen";var cn=Date.UTC(2020,0,1),ln=Date.UTC(2100,0,1),un=720*60*60*1e3,dn=e=>!sn(e)&&isFinite(e)&&e>=cn&&e<=ln,fn=e=>dn(e)?e:Date.now(),pn=e=>(e??0)*1e3,mn=e=>isFinite(e)&&e>=0&&e<=un;import{PlaybackState as hn}from"@vkontakte/videoplayer-core/evergreen";import{Observable as gn,Subscription as _n,Subject as vn,fromEvent as yn,isNullable as bn,map as xn,merge as Y,filter as Sn,filterChanged as Cn,getWindow as wn}from"@vkontakte/videoplayer-shared/evergreen";var Tn=1440*60*1e3,En=(e,t,n)=>new gn(r=>{let i=new _n,a,o=!1;i.add(e.info.isLive$.pipe(Cn()).subscribe(e=>{a=void 0,o=!!e}));let{playing$:s,paused$:c,ended$:l,looped$:u}=e.events,d=e.events.willSeek$.pipe(Sn(()=>e.info.playbackState$.getValue()===hn.PLAYING)),f=e.events.seeked$.pipe(Sn(()=>e.info.playbackState$.getValue()===hn.PLAYING)),p=e.info.isStalled$.pipe(Sn(e=>e)),m=!1,h=new vn;i.add(d.subscribe(()=>{m||h.next(),m=!0})).add(f.subscribe(()=>m=!1));let g=Y(yn(wn(),`beforeunload`),e.events.willDestruct$),_=e=>{a=e},v=e=>{let t=a;if(a=void 0,!(bn(t)||bn(e)||!isFinite(t)||!isFinite(e))){if(Math.round(e)<=Math.round(t)){a=e;return}if(e-t>Tn){n.onInvalidInterval?.({from:t,to:e});return}r.next({from:t,to:e})}},y=new vn,b=new vn,ee=()=>o?fn(n.exactTime?pn(e.getExactLiveTime()):e.info.liveTime$.getValue()):pn(n.exactTime?e.getExactTime():e.info.position$.getValue()),te=Y(s,f,y).pipe(xn(()=>ee())),x=Y(u,Y(c,h,g,l,b,p).pipe(xn(()=>ee()))),ne=t.started$&&t.ended$?Y(te,t.ended$):te,re=t.started$&&t.ended$?Y(x,t.started$):x;if(i.add(ne.subscribe(_)).add(re.subscribe(v)),n.heartbeatInterval&&isFinite(n.heartbeatInterval)){let e=[],t=()=>{b.next(),y.next()};i.add(ne.subscribe(()=>{let r=O.setTimeout(t,n.heartbeatInterval);e.push(r)})).add(re.subscribe(()=>{e.forEach(e=>{O.clearTimeout(e)}),e=[]}))}return i});import{Subscription as Dn}from"@vkontakte/videoplayer-core/evergreen";import{Observable as On,merge as kn}from"@vkontakte/videoplayer-shared/evergreen";var An=(e,t)=>new On(n=>{let r=new Dn;return r.add(kn(e.info.position$,e.info.liveTime$).subscribe(()=>{let e=t.getTotalViewTime();e>=t.targetDuration&&(n.next({target_duration:t.targetDuration,current_tvt:e}),r.unsubscribe())})),r});import{Subscription as jn}from"@vkontakte/videoplayer-core/evergreen";import{Observable as Mn,filter as Nn}from"@vkontakte/videoplayer-shared/evergreen";var Pn=(e,{maxTimeWindow:t})=>new Mn(n=>{let r=new jn,i={maxBytesThreshold:100*1024*1024,minBytesThreshold:100*1024,maxTimeWindow:t,minTimeWindow:2e3},a={bytes:0,startTime:0,lastEmitTime:0,timeoutId:0},o=()=>{a.timeoutId&&=(O.clearTimeout(a.timeoutId),null)},s=()=>{a.bytes=0,a.startTime=0,a.lastEmitTime=0,o()},c=e=>{if(a.bytes<i.minBytesThreshold||e-a.lastEmitTime<i.minTimeWindow&&a.lastEmitTime>0)return!1;let t=(a.startTime>0?e-a.startTime:0)>=i.maxTimeWindow,n=a.bytes>=i.maxBytesThreshold;return t||n},l=(e=!1)=>{let t=Date.now();if(!e&&!c(t))return;o();let r=a.startTime>0?t-a.startTime:Math.max(i.minTimeWindow,1),l=a.bytes/(r/1e3);n.next({download_bytes:Math.round(a.bytes),download_speed:Math.round(l)}),a.lastEmitTime=t,s()},u=()=>{if(o(),a.startTime>0&&a.bytes>=i.minBytesThreshold){let e=Date.now()-a.startTime,t=i.maxTimeWindow-e;t>100?a.timeoutId=O.setTimeout(()=>{a.timeoutId=null,l()},t):t>0&&l()}};return r.add(e.info.httpDownloadMetrics$.pipe(Nn(e=>e!==void 0)).subscribe(e=>{let t=Date.now();a.startTime===0&&(a.startTime=t),a.bytes+=e.bytes,c(t)?l():u()})),r.add(()=>{if(o(),a.bytes>0){let e=Date.now(),t=a.startTime>0?e-a.startTime:i.minTimeWindow,r=a.bytes/(t/1e3);n.next({download_bytes:Math.round(a.bytes),download_speed:Math.round(r)})}s()}),r});import{Observable as Fn,Subscription as In,merge as Ln}from"@vkontakte/videoplayer-shared/evergreen";var Rn=e=>new Fn(t=>{let n=new In,r=0,i=1,a=0,o=!1,s=!1,c=()=>{if(!o)return;let e=Date.now();a+=(e-r)*i,r=e},l=()=>{(!o||s)&&(r=Date.now(),o=!0,s=!1)},u=()=>{o&&=(c(),!1)},d=()=>{o&&(c(),o=!1,s=!0)},f=e=>{o&&c(),i=e,r=Date.now()},{started$:p,playing$:m,paused$:h,willSeek$:g,ended$:_,willDestruct$:v}=e.events,{currentPlaybackRate$:y}=e.info;return n.add(g.subscribe(d)).add(Ln(p,m).subscribe(l)).add(Ln(h,_,v).subscribe(u)).add(y.subscribe(f)),t.next(()=>{let e=a;return o&&(e+=(Date.now()-r)*i),Math.round(e)}),n});import{Observable as zn,Subscription as Bn,merge as Vn}from"@vkontakte/videoplayer-shared/evergreen";var Hn=(e,t)=>new zn(n=>{let r=new Bn,i=0;return r.add(Vn(e.info.position$,e.info.liveTime$).subscribe(()=>{let e=t.getTotalViewTime();e>i&&Math.floor(e/t.interval)>Math.floor(i/t.interval)&&(i=e,n.next())})),r});import{assertNever as Un,isNullable as Wn}from"@vkontakte/videoplayer-shared/evergreen";import{HttpConnectionType as Gn,Surface as X,VideoFormat as Z,VideoQuality as Q}from"@vkontakte/videoplayer-core/evergreen";var Kn=e=>{if(!Wn(e)){if(typeof e==`string`)return e;try{return JSON.stringify(e)}catch{return String(e)}}},qn=e=>e&&{[Gn.HTTP1]:`http1`,[Gn.HTTP2]:`http2`,[Gn.QUIC]:`http3`}[e],Jn=e=>{if(e!==void 0)switch(e){case Z.MPEG:return`MP4`;case Z.DASH:case Z.DASH_LIVE:return`DASH`;case Z.DASH_SEP:return`DASH_SEP`;case Z.DASH_ONDEMAND:return`ONDEMAND_DASH`;case Z.HLS_ONDEMAND:return`ONDEMAND_HLS`;case Z.HLS:case Z.HLS_LIVE:return`HLS`;case Z.HLS_FMP4:return`HLS_FMP4`;case Z.DASH_WEBM:case Z.DASH_LIVE_WEBM:return`WEBM`;case Z.DASH_LIVE_CMAF:return`ONDEMAND_DASH_LIVE`;case Z.HLS_LIVE_CMAF:return`ONDEMAND_HLS_LIVE`;case Z.WEB_RTC_LIVE:return`WEBRTC`;case Z.DASH_WEBM_AV1:return`AV1`;case Z.DASH_STREAMS:return`MULTI_DASH`;default:return Un(e)}},$=e=>{if(e!==void 0)switch(e){case Q.Q_144P:return`144p`;case Q.Q_240P:return`240p`;case Q.Q_360P:return`360p`;case Q.Q_480P:return`480p`;case Q.Q_720P:return`720p`;case Q.Q_1080P:return`1080p`;case Q.Q_1440P:return`1440p`;case Q.Q_2160P:return`2160p`;case Q.INVARIANT:case Q.Q_576P:case Q.Q_4320P:return`UNKNOWN`;default:return Un(e)}},Yn=e=>{if(e!==void 0)switch(e){case`MP4`:case`ONDEMAND_DASH`:case`ONDEMAND_DASH_LIVE`:case`ONDEMAND_HLS`:case`ONDEMAND_HLS_LIVE`:case`MULTI_DASH`:case`WEBM`:case`AV1`:return`vod`;case`DASH`:case`HLS`:case`HLS_FMP4`:case`WEBRTC`:return`live`;default:return}},Xn=(e,t,n)=>{if(e&&t)switch(e){case Z.MPEG:return n&&t[e]&&t[e][n];default:return t[e]?.url}},Zn=(e,t=!1)=>{if(t)return`minimal`;switch(e){case void 0:case X.NONE:return;case X.INLINE:return`inline`;case X.FULLSCREEN:return`fullscreen`;case X.SECOND_SCREEN:return`chromecast`;case X.PIP:return`pip_external`}},Qn=e=>{switch(e){case void 0:return;case X.INVISIBLE:return`background`;default:return`foreground`}},$n=`_thin-one-stat_deviceId`,er=`2.2.0`,tr=-1,nr=()=>{let e=new B;return{subscription:e,subscribe:(t,n)=>{t&&e.add(t.subscribe(n))}}};class rr{apis=[];logger;config;subscription;lifecycleFlushSubscription;timeSynchronisation;statContext;deviceId;targetDuration;debugLogger=new mt;thinOneStatDebugLog=this.debugLogger.createComponentLog(`ThinOneStat`);onceEvents=new Set(Ot);sentOnceEvents=new Set;eventNumber=1;sessionMode=`common`;isStarted;isSeeking;inBufferStarvation;isLive;lastContentType;cdnHostname;isEmbed;embedHostname;connectionType;connectionReused;player;loopCount=0;loopLimitReached=!1;playerSize={width:0,height:0};currentSubtitle;userQuality;downloadQuality;downloadSpeed;networkType;uiEvents;playerSubscription;uiSubscription;adsSubscription;seekAction$=new wt(`unknown`);vsid$=new wt(void 0);isid$=new wt(void 0);errors$=new Ct;getTotalViewTime=()=>0;constructor(e,t){this.statContext=e,this.config=It(t.config??{}),this.subscription=new B,this.config.synchronizeTime&&(this.timeSynchronisation=new on),this.subscription.add(this.errors$.subscribe(e=>{this.thinOneStatDebugLog({message:`[${e.category}] ${e.id}: ${e.message}`})}));let n=xt.get($n);this.statContext.deviceId?this.deviceId=this.statContext.deviceId:n?this.deviceId=n:(this.deviceId=an(),xt.set($n,this.deviceId)),this.statContext.targetDuration?this.targetDuration=this.statContext.targetDuration:this.targetDuration=this.config.watchedNDefaultTargetDuration,this.resetViewSession(),t.useIsid&&this.isid$.next(t.isid??ge());let r=new Jt({errors$:this.errors$,config:this.config,timeSynchronisation:this.timeSynchronisation});this.config.apiForticom&&this.apis.push(new Wt({config:this.config,apiTransport:r,errors$:this.errors$,deviceId:this.deviceId,refreshAuthToken:t.refreshAuthToken})),this.config.apiUvRest&&this.apis.push(new Gt({config:this.config,apiTransport:r,errors$:this.errors$,userId:this.statContext.userId,deviceId:this.deviceId,getAuthToken:t.getUvRestAuthToken,timeSynchronisation:this.timeSynchronisation})),this.logger=new tn({config:this.config,debugLogger:this.debugLogger,apis:this.apis,errors$:this.errors$,userSalt:t.userSalt});let{isEmbed:i,topOrigin:a}=ft();this.isEmbed=i,this.embedHostname=a?new URL(a).hostname:void 0}authorize(e){this.apis.forEach(t=>{t.authorize(e).catch(()=>{})})}reportError(e){let t={error_severity:e.severity,error_category:this.mapErrorCategory(e.category),player_error_code:e.code,player_error_trace:e.trace,error_message:e.message,content_type:this.getCurrentContentType()};this.logError(t),e.severity===`critical`&&this.logCannotPlay(),this.logTelemetry(this.collectTelemetryData())}updateContext(e){this.statContext={...this.statContext,...e}}attachTo(e,t){this.playerSubscription&&(this.thinOneStatDebugLog({message:`Re-attaching to player, unsubscribing from previous`}),this.playerSubscription.unsubscribe());let n=new B,r=(e,t)=>n.add(e.subscribe(t));r(Rn(e),e=>{this.getTotalViewTime=e}),r(e.info.isLive$,e=>this.isLive=e),r(e.info.currentFormat$,e=>{let t=Jn(e);t&&(this.lastContentType=t)});let i;r(e.info.hostname$.pipe(z(e=>e!==void 0),gt()),e=>{if(i!==void 0&&i!==e){let t={cdn_host:e,connection_type:this.connectionType,connection_reused:this.connectionReused,content_type:this.getCurrentContentType()};this.logFailover(t),this.logTelemetry(this.collectTelemetryData())}this.cdnHostname=i=e}),r(e.info.httpConnectionType$,e=>{this.connectionType=qn(e)}),r(e.info.httpConnectionReused$,e=>{this.connectionReused=e}),r(En(e,t,{heartbeatInterval:this.config.watchCoverageHeartbeatInterval,exactTime:this.config.watchCoverageExactTime,onInvalidInterval:({from:e,to:t})=>this.thinOneStatDebugLog({message:`invalid watch interval: ${Math.round(e)}-${Math.round(t)}`})}),t=>{let n={watch_interval:`${Math.round(t.from)}-${Math.round(t.to)}`,in_history:!!this.statContext.inHistory,content_type:this.getCurrentContentType(),playback_quality:$(e.info.currentQuality$.getValue()),recom_info:this.statContext.recomInfo};if(!e.info.isLive$.getValue())this.logWatchCoverage(n);else{let t=e.info.atLiveEdge$.getValue();this.logWatchCoverageLive({...n,live:!!t})}}),r(Hn(e,{interval:this.statContext.telemetryInterval??this.config.telemetryInterval,getTotalViewTime:this.getTotalViewTime}),()=>{this.logTelemetry(this.collectTelemetryData())}),r(An(e,{targetDuration:this.targetDuration,getTotalViewTime:this.getTotalViewTime}),e=>{this.logWatchedN(e)}),r(ht(e.events.willReady$,e.events.willStart$),()=>{let e={isid:this.isid$.getValue(),stream_profile:Yn(this.getCurrentContentType()),dpi:Math.round(this.statContext.dpi??96*H.display.pixelRatio),web_layout:this.statContext.isMobile||H.device.isMobile?`mobile`:`desktop`,preloaded:this.statContext.preload,navigation:this.statContext.navigation,recom_info:this.statContext.recomInfo};this.isEmbed&&(e.iframe_host=this.embedHostname??`unknown`),this.logStartSession(e)}),r(e.events.willReady$,()=>{if(this.statContext.preload){let t=e.info.currentFormat$.getValue(),n=e.info.availableSources$.getValue(),r=e.info.currentVideoBuffer$.getValue(),i=e.info.currentQuality$.getValue(),a={target_buffer_time:this.calcBufferTime(r),playback_url:Xn(t,n,i),playback_quality:$(i)};this.logPreloadStarted(a)}});let a=()=>({playback_url:Xn(e.info.currentFormat$.getValue(),e.info.availableSources$.getValue(),e.info.currentQuality$.getValue()),cdn_host:this.cdnHostname,connection_type:this.connectionType,connection_reused:this.connectionReused});r(R({manifestRequested$:e.events.manifestRequested$,connectionType:e.info.httpConnectionType$,connectionReused:e.info.httpConnectionReused$}).pipe(vt()),()=>{this.logManifestRequest(a())}),r(R({firstBytesManifest:e.events.firstBytesManifest$,connectionType:e.info.httpConnectionType$,connectionReused:e.info.httpConnectionReused$}).pipe(vt()),()=>{this.logFirstByteManifest(a())}),r(e.events.manifestReceived$,()=>{this.logManifestReceived(a())}),r(e.events.firstBytesRequested$,()=>{this.logFirstMediaRequest(a())}),r(e.events.firstBytes$,()=>{this.logFirstByteMedia(a())}),r(e.events.willStart$,()=>{this.isStarted=!0}),r(e.events.started$,()=>{let t=e.info.currentVideoBuffer$.getValue(),n=e.info.currentQuality$.getValue(),r=e.info.isAutoQualityEnabled$.getValue(),i=e.info.muted$.getValue(),a=e.info.volume$.getValue(),o=e.info.surface$.getValue(),s=e.info.availableTextTracks$.getValue(),c=e.info.currentTextTrack$.getValue(),l=e.info.currentAudioStream$.getValue(),u=e.info.currentPlaybackRate$.getValue(),d={playback_quality:$(n),user_quality:r?`auto`:$(n),player_width:Math.round(this.statContext.playerSize?.width??this.playerSize.width),player_height:Math.round(this.statContext.playerSize?.height??this.playerSize.height),muted:i,sound_volume:Math.round(a*100),buffer_time:this.calcBufferTime(t),mode:Zn(o,this.statContext.isPreviewPlayerView),visibility:Qn(o),subtitles_enabled:c?!!c:this.currentSubtitle?.enabled,auto_subtitles:s.find(({id:e})=>e===c)?.isAuto??this.currentSubtitle?.auto,audio_track_lang:l?.language,playback_rate:u*100,recom_info:this.statContext.recomInfo};this.logPlaybackStarted(d),this.logTelemetry(this.collectTelemetryData())}),r(e.events.willStart$,()=>{this.logPlay()}),r(e.events.willResume$,()=>{this.logResume()}),r(e.events.playing$,()=>{let t=e.info.currentVideoBuffer$.getValue(),n={buffer_time:this.calcBufferTime(t)};this.logPlaying(n),this.inBufferStarvation=!1}),r(e.events.paused$,()=>{this.logPause()}),r(ht(e.events.ended$,e.events.looped$),()=>{this.logContentEndReached(),++this.loopCount>this.config.maxLoops&&(this.loopLimitReached=!0)}),r(e.events.willStop$,()=>{this.isStarted=!1,this.isSeeking=!1,this.inBufferStarvation=!1,this.logStop()}),r(ht(e.events.fetcherRecoverableError$,e.events.fatalError$),()=>{this.statContext.preload&&this.logPreloadError()}),r(e.events.fetcherRecoverableError$,({id:e,category:t,data:n,message:r,httpCode:i,UVBackendErrorCode:a})=>{let o={error_severity:this.mapErrorSeverity(t),error_category:this.mapErrorCategory(t),player_error_code:e,player_error_trace:Kn(n),http_error_code:i,uv_backend_error_code_subcode:a,error_message:r,content_type:this.getCurrentContentType()};this.logError(o),this.logTelemetry(this.collectTelemetryData())}),r(e.events.managedError$,({id:e,category:t,data:n,message:r,httpCode:i,UVBackendErrorCode:a})=>{let o={error_severity:this.mapErrorSeverity(t),error_category:this.mapErrorCategory(t),player_error_code:e,player_error_trace:Kn(n),http_error_code:i,uv_backend_error_code_subcode:a,error_message:r,content_type:this.getCurrentContentType()};this.logError(o),this.logTelemetry(this.collectTelemetryData())}),r(e.events.fatalError$,({id:e,category:t,data:n,message:r,httpCode:i,UVBackendErrorCode:a})=>{let o={error_severity:`critical`,error_category:this.mapErrorCategory(t),player_error_code:e,player_error_trace:Kn(n),http_error_code:i,uv_backend_error_code_subcode:a,error_message:r,content_type:this.getCurrentContentType()};this.logError(o),this.logCannotPlay(),this.logTelemetry(this.collectTelemetryData())}),r(R({connectionType:e.info.httpConnectionType$,connectionReused:e.info.httpConnectionReused$,connectionMetrics:e.info.httpConnectionMetrics$}).pipe(z(({connectionReused:e})=>e!==void 0),z(({connectionMetrics:e})=>e!==void 0),gt((e,t)=>e.connectionType===t.connectionType)),({connectionMetrics:t})=>{let n=e.info.currentFormat$.getValue(),r=e.info.availableSources$.getValue(),i=e.info.currentQuality$.getValue(),a=e.info.rttEstimation$.getValue();this.networkType=this.statContext.networkType??t?.networkType;let o=t?.dnsResolveTime,s=t?.tcpHandshakeTime,c=t?.tlsHandshakeTime,l={playback_url:Xn(n,r,i),cdn_host:this.cdnHostname,connection_type:this.connectionType,connection_reused:this.connectionReused,rtt:a?Math.round(a):void 0,network_type:this.networkType,dns_resolve_time:o?Math.round(o):void 0,tcp_handshake_time:s?Math.round(s):void 0,tls_handshake_time:c?Math.round(c):void 0};this.logConnectionEstablished(l)}),r(Pn(e,{maxTimeWindow:this.config.downloadBytesMaxTime}),e=>{this.downloadSpeed=e.download_speed,this.logDownloadBytes(e)}),r(e.events.firstFrame$,()=>{this.logFirstVideoFrameDecoded()}),r(e.events.willSeek$,()=>{if(this.isSeeking||!this.isStarted)return;this.isSeeking=!0;let e={seek_type:this.seekAction$.getValue()};this.logSeeking(e)}),r(e.events.seeked$,()=>{if(!this.isSeeking)return;this.isSeeking=!1;let e={seek_type:this.seekAction$.getValue()};this.logSeeked(e),this.seekAction$.next(`unknown`)}),r(e.info.stallStartTime$,t=>{if(t<=0){this.inBufferStarvation=!1;return}!this.isStarted||this.inBufferStarvation||(this.inBufferStarvation=!0,this.logBufferStarvation({buffer_time:this.calcBufferTime(e.info.currentVideoBuffer$.getValue())}),this.logTelemetry(this.collectTelemetryData()))}),r(e.info.currentVideoBuffer$.pipe(z(e=>!!this.calcBufferTime(e)),vt()),e=>{let t=this.calcBufferTime(e);if(this.statContext.preload){let e={buffer_time:t};this.logPreloadEnded(e)}this.logReady({buffer_time:t})});let o;r(R({videoStream:e.info.currentVideoStream$,audioStream:e.info.currentAudioStream$,quality:e.info.currentQuality$}).pipe(z(({videoStream:e,audioStream:t})=>e!==void 0&&t!==void 0),z(({quality:e})=>e!==void 0)),({videoStream:t,audioStream:n,quality:r})=>{if(o!==r){let i=e.info.isAutoQualityEnabled$.getValue();if(this.userQuality=i?`auto`:$(r),this.downloadQuality=$(r),this.isStarted){let e=t?.codec,r=n?.codec,i={user_quality:this.userQuality,playback_quality:$(o),download_quality:this.downloadQuality,codec_info:this.genCodecInfo(e,r)};this.logQualityChanged(i)}o=r}});let s,c;return r(e.info.surface$,e=>{let t=Zn(e,this.statContext.isPreviewPlayerView);t&&!this.uiEvents&&this.isStarted&&this.logModeChanged({mode:t}),c=Qn(e),s!==c&&(this.isStarted&&this.logVisibilityChanged({visibility:c}),s=Qn(e))}),r(R({muted:e.info.muted$,volume:e.info.volume$}).pipe(bt(300)),({muted:e,volume:t})=>{if(this.isStarted){let n={muted:e,sound_volume:Math.round(t*100)};this.logSoundChanged(n)}}),r(e.info.currentFormat$.pipe(z(e=>e!==void 0),yt(1)),e=>{let t={content_type:Jn(e),cdn_host:this.cdnHostname,connection_type:this.connectionType,connection_reused:this.connectionReused};this.logContentTypeChange(t)}),r(e.info.currentAudioStream$.pipe(z(e=>e!==void 0),yt(1)),e=>{let t={audio_track_lang:e.language,codec_info:e.codec};this.logAudioTrackSwitched(t)}),r(e.info.currentVideoStream$.pipe(z(e=>e!==void 0),yt(1)),e=>{let t={hdr:e.hdr,codec_info:e.codec};this.logVideoTrackSwitched(t)}),this.player=e,this.playerSubscription=n,this.resubscribeLifecycleFlush(),this.subscription.add(n),n}attachToUi(e){this.uiSubscription&&(this.thinOneStatDebugLog({message:`Re-attaching to UI, unsubscribing from previous`}),this.uiSubscription.unsubscribe()),this.uiEvents=e;let{subscribe:t,subscription:n}=nr();return t(e.actionSeek$,this.seekAction$),t(e.playerSize$,({width:e,height:t})=>{e&&t&&(this.playerSize={width:e,height:t},this.isStarted&&this.logViewPortChanged({player_width:Math.round(e),player_height:Math.round(t)}))}),t(e.isLoaderVisible$,e=>{if(!e){this.inBufferStarvation=!1;return}this.isStarted&&(this.logShowLoader(),!(this.isSeeking||this.inBufferStarvation||!this.player)&&(this.inBufferStarvation=!0,this.logBufferStarvation({buffer_time:this.calcBufferTime(this.player.info.currentVideoBuffer$.getValue())}),this.logTelemetry(this.collectTelemetryData())))}),t(e.actionSubtitlesSwitched$,e=>{if(this.currentSubtitle={...e},this.isStarted){let t={subtitles_enabled:e.enabled,subtitles_track_lang:e.lang,auto_subtitles:e.auto};this.logSubtitlesSwitched(t)}}),it({uiEvents:this.uiEvents,initExternalSubscribers:e=>{t(e,e=>{this.isStarted&&this.logModeChanged({mode:Zn(e)})})},subscribe:t,player:this.player}),t(e.actionQuality$,e=>{let t=e=>Object.values(kt).includes(e),n=this.player?.info.currentVideoStream$.getValue()?.codec,r=this.player?.info.currentAudioStream$.getValue()?.codec,i={user_quality:t(e)?`auto`:$(e),playback_quality:$(this.player?.info.currentQuality$.getValue()),download_quality:t(e)?$(this.player?.info.currentQuality$?.getValue()):$(e),codec_info:this.genCodecInfo(n,r)};this.logQualityChangeRequested(i)}),t(e.fatalError$,e=>{if(!e)return;let{type:t,message:n}=e,r={severity:`critical`,category:V.FATAL,code:t,message:n};this.reportError(r)}),this.uiSubscription=n,this.resubscribeLifecycleFlush(),this.subscription.add(n),n}attachToAds(e){this.adsSubscription&&(this.thinOneStatDebugLog({message:`Re-attaching to ads, unsubscribing from previous`}),this.adsSubscription.unsubscribe());let{subscription:t,subscribe:n}=nr();return n(e.init$,e=>{let t={slot:e};this.logAdvConfiguration(t)}),n(e.slotRequested$,e=>{let t={adv_section:e};this.logAdvRequest(t)}),n(e.started$,e=>{let t={adv_section:e};this.logAdvBreakStarted(t)}),n(e.ended$,({section:e,ordIds:t})=>{let n={adv_section:e,erids:t.join(`,`)};this.logAdvBreakEnded(n)}),n(e.error$,e=>{let t={error_message:e};this.logAdvError(t)}),this.adsSubscription=t,this.resubscribeLifecycleFlush(),this.subscription.add(t),t}setSessionMode(e){this.sessionMode=e,this.thinOneStatDebugLog({message:`session mode set to ${e}`})}pause(){this.logger.pause(),this.thinOneStatDebugLog({message:`paused`})}resume(){this.logger.resume(),this.thinOneStatDebugLog({message:`resumed`})}getDeviceId(){return this.deviceId}resetViewSession(){this.thinOneStatDebugLog({message:`VSID reset`}),T.generateVSID(this.config.useVsid64),this.vsid$.next(T.getVSID()),this.eventNumber=0,this.loopCount=0,this.loopLimitReached=!1,this.sentOnceEvents.clear()}async destroy(){this.thinOneStatDebugLog({message:`Destroying ThinOneStat`}),this.subscription.unsubscribe(),this.lifecycleFlushSubscription?.unsubscribe(),await this.logger.destroy(),this.apis.forEach(e=>e.destroy()),this.sentOnceEvents.clear(),this.thinOneStatDebugLog({message:`ThinOneStat destroyed`})}calcBufferTime(e){return e?Math.round(((e.end??0)-(e.start??0))*1e3):void 0}mapErrorCategory(e){switch(e){case V.NETWORK:return`network`;case V.VIDEO_PIPELINE:return`video_pipeline`;case V.EXTERNAL_API:return`external_api`;case V.PARSER:return`parser`;case V.DOM:return`dom`;default:return`wtf`}}mapErrorSeverity(e){return V.FATAL===e?`critical`:`informative`}getCurrentContentType(){let e=Jn(this.player?.info.currentFormat$.getValue());if(e)return e;if(this.lastContentType)return this.lastContentType;let t=this.player?.info.availableSources$.getValue();if(!t)return;let n=Object.keys(t)[0];return Jn(n)}genCodecInfo(e,t){return e&&t?`${e},${t}`:void 0}getLatencyInfo(){let e=this.player?.info.atLiveEdge$.getValue();return JSON.stringify({client_time:Date.now(),live:e})}collectTelemetryData(){if(!this.player)return{};let e=this.player.info.currentVideoBuffer$.getValue(),t=this.player.experimental.element$.getValue(),n=0,r=0;t&&(n=t.getVideoPlaybackQuality().droppedVideoFrames,r=t.getVideoPlaybackQuality().totalVideoFrames);let i=this.getCurrentContentType(),a=this.player.info.currentVideoStream$.getValue()?.codec,o=this.player.info.currentAudioStream$.getValue()?.codec,s=this.player.info.videoBitrate$.getValue(),c=this.player.info.rttEstimation$.getValue();return{buffer_time:this.calcBufferTime(e),dropped_frames_count:n,rendered_frames_count:r,content_type:i,user_quality:this.userQuality,playback_quality:$(this.player.info.currentQuality$.getValue()),download_quality:this.downloadQuality,codec_info:this.genCodecInfo(a,o),bandwidth_from_manifest:s?Math.round(s):void 0,cdn_host:this.cdnHostname,download_speed:this.downloadSpeed?Math.round(this.downloadSpeed):void 0,network_type:this.networkType,rtt:c?Math.round(c):void 0,latency:this.getLatencyInfo()}}getPlaybackPosition(){if(_t(this.player))return tr;if(!this.isLive){let e=pn(this.player.info.position$.getValue());return mn(e)?Math.round(e):tr}let e=this.player.info.liveTime$.getValue();return _t(e)?tr:Math.round(fn(e))}isValidPlatform(e=``){return[`web:desktop`,`web:mobile`,`smart_tv`].includes(e)}createRequiredParams(e){let t=this.vsid$.getValue();dt(t);let n=this.eventNumber++,r={vsid:t,uv_movie_id:this.statContext.movieId,event_name:e,client_time:Date.now(),application:this.statContext.application??`@vkontakte/videoplayer-statistics:${E}`,platform:this.isValidPlatform(this.statContext.platform)?this.statContext.platform:`web:${this.statContext.isMobile||H.device.isMobile?`mobile`:`desktop`}`,product:this.statContext.product,event_number:n,playback_position:this.getPlaybackPosition(),current_tvt:this.getTotalViewTime(),cdn_host:this.cdnHostname};for(let e of this.config.disabledParams)delete r[e];return r}createRequiredFatParams(){let e={stats_version:er,browser:this.statContext.browser??H.browser.current,browser_version:this.statContext.browserVersion??String(H.browser.currentVersion),os:this.statContext.os??H.device.os.name,os_version:this.statContext.osVersion??H.device.os.version,device_type:this.statContext.deviceType??(this.statContext.isMobile||H.device.isMobile?`mobile`:`desktop`),device_manufacturer:this.statContext.deviceManufacturer??H.device.details.vendor,device_model:this.statContext.deviceModel??H.device.details.model,navigation:this.statContext.navigation};for(let t of this.config.disabledParams)delete e[t];return e}log(e,t,n=!1){if(this.sessionMode===`adv`&&!rn(e)||this.config.disabledEvents.includes(e)||this.loopLimitReached&&!Dt.includes(e))return;if(this.onceEvents.has(e)){if(this.sentOnceEvents.has(e))return;this.sentOnceEvents.add(e)}let r=this.createRequiredParams(e),i={};n&&(i=this.createRequiredFatParams()),this.logger.log({...r,...i,...t})}logWatchCoverage(e){this.log(`watch_coverage`,e,!0)}logWatchCoverageLive(e){this.log(`watch_coverage_live`,e,!0)}logWatchedN(e){this.log(`watched_n`,e,!0)}logStartSession(e){this.log(`start_session`,e,!0)}logPreloadStarted(e){this.log(`preload_started`,e)}logPreloadEnded(e){this.log(`preload_ended`,e)}logPreloadError(){this.log(`preload_error`)}logPlaybackStarted(e){this.log(`playback_started`,e,!0)}logPlay(){this.log(`play`)}logManifestRequest(e){this.log(`manifest_request`,e)}logFirstByteManifest(e){this.log(`first_byte_manifest`,e)}logFirstMediaRequest(e){this.log(`first_media_request`,e)}logFirstByteMedia(e){this.log(`first_byte_media`,e)}logManifestReceived(e){this.log(`manifest_received`,e)}logConnectionEstablished(e){this.log(`connection_established`,e)}logFirstVideoFrameDecoded(){this.log(`first_video_frame_decoded`)}logReady(e){this.log(`ready`,e)}logPlaying(e){this.log(`playing`,e)}logPause(){this.log(`pause`)}logResume(){this.log(`resume`)}logSeeking(e){this.log(`seeking`,e)}logSeeked(e){this.log(`seeked`,e)}logContentEndReached(){this.log(`content_end_reached`)}logStop(){this.log(`stop`)}logBufferStarvation(e){this.log(`buffer_starvation`,e)}logShowLoader(){this.log(`show_loader`)}logQualityChangeRequested(e){this.log(`quality_change_requested`,e)}logQualityChanged(e){this.log(`quality_changed`,e)}logCdnHostChanged(e){this.log(`cdn_host_changed`,e)}logViewPortChanged(e){this.log(`view_port_changed`,e)}logModeChanged(e){this.log(`mode_changed`,e)}logVisibilityChanged(e){this.log(`visibility_changed`,e)}logAudioTrackSwitched(e){this.log(`audio_track_switched`,e)}logVideoTrackSwitched(e){this.log(`video_track_switched`,e)}logSubtitlesSwitched(e){this.log(`subtitles_switched`,e)}logSoundChanged(e){this.log(`sound_changed`,e)}logFailover(e){this.log(`failover`,e)}logContentTypeChange(e){this.log(`content_type_change`,e)}logError(e){this.log(`error`,e,!0)}logCannotPlay(){this.log(`cannot_play`,{},!0)}logDownloadBytes(e){this.log(`download_bytes`,e)}logTelemetry(e){this.log(`telemetry`,e)}logAdvConfiguration(e){this.log(`adv_configuration`,e)}logAdvRequest(e){this.log(`adv_request`,e)}logAdvBreakStarted(e){this.log(`adv_break_started`,e)}logAdvBreakEnded(e){this.log(`adv_break_ended`,e)}logAdvError(e){this.log(`adv_error`,e)}resubscribeLifecycleFlush(){this.lifecycleFlushSubscription?.unsubscribe();let e=St(),t=()=>{this.logger.flush({wantBeacon:!0,clearStorage:this.config.clearStorageAtUnload}).catch(()=>{})},n=new B;n.add(pt(e,`beforeunload`).subscribe(t)),n.add(pt(e,`pagehide`).subscribe(t)),n.add(pt(e.document,`visibilitychange`).subscribe(e=>{e.target?.visibilityState===`hidden`&&t()})),this.lifecycleFlushSubscription=n}}import{PlaybackState as ir,Subscription as ar}from"@vkontakte/videoplayer-core/evergreen";import{Logger as or}from"@vkontakte/videoplayer-shared/evergreen";class sr{startTime=null;stopTime=null;accumulatedTime=0;get time(){return Math.floor(this.timeMs/1e3)}get timeMs(){return this.startTime===null?0:(this.stopTime??this.now())-this.startTime-this.accumulatedTime}get isRunning(){return this.startTime!==null&&this.stopTime===null}start(){this.isRunning||(this.stopTime===null?this.startTime=this.now():(this.accumulatedTime+=this.now()-this.stopTime,this.stopTime=null))}stop(){this.isRunning&&(this.stopTime=this.now())}reset(){this.startTime=null,this.stopTime=null,this.accumulatedTime=0}now(){return Date.now()}}var cr=()=>window.Image?new Image:document.createElement(`img`);class lr{params;position=0;started=!1;isActiveLive;heartbeatPixels=[];heartbeatLastTimeSent={};heartbeatInterval;heartbeatFirstTimeoutId;idleCallbackIds=[];log;stopwatch=new sr;subscription=new ar;constructor(e){this.params=e;let t=new or;this.log=t.createComponentLog(`MediascopePixel`)}attachTo(e){this.subscription.add(e.info.playbackState$.subscribe(e=>this.onPlaybackState(e))).add(e.info.position$.subscribe(e=>this.onPosition(e))).add(e.info.atLiveEdge$.subscribe(e=>this.isActiveLive=e)),this.subscription.add(e.events.willSeek$.subscribe(()=>this.onWillSeek())).add(e.events.seeked$.subscribe(()=>this.onSeeked())).add(e.events.ended$.subscribe(()=>this.onEnded()))}destroy(){this.stopwatch.reset(),this.subscription.unsubscribe(),O.clearTimeout(this.heartbeatFirstTimeoutId),this.idleCallbackIds.forEach(e=>window.cancelIdleCallback(e)),this.send(`stop`)}onPlaybackState(e){e===ir.PLAYING?this.started?this.play():this.start():e===ir.PAUSED&&this.pause()}start(){this.started=!0,this.stopwatch.start(),this.send(`start`),this.heartbeatPixels=this.prepareHeartbeatPixels(),this.heartbeatInterval=this.heartbeatPixels[0]?.interval;let e=this.heartbeatPixels[0]?.url,t=this.heartbeatPixels[0]?.delay;if(t!==void 0&&e)try{this.heartbeatFirstTimeoutId=O.setTimeout(()=>{this.sendHeartbeat(e)},t*1e3)}catch(e){this.log({message:e.message})}}play(){this.stopwatch.start(),this.send(`resume`)}pause(){this.stopwatch.stop(),this.send(`pause`)}onPosition(e){if(this.position=e,this.heartbeatInterval&&this.stopwatch.time!==0&&this.stopwatch.time%this.heartbeatInterval===0)for(let[e,{url:t}]of this.heartbeatPixels.entries())this.heartbeatLastTimeSent[e]!==this.stopwatch.time&&(this.heartbeatLastTimeSent[e]=this.stopwatch.time,this.sendHeartbeat(t))}onWillSeek(){this.started&&this.send(`stop`)}onSeeked(){this.started&&this.send(`start`)}onEnded(){this.stopwatch.stop(),this.send(`stop`)}validatePixels(e,t){return e?.filter(e=>e.event===t.event&&t.keys.every(t=>!!e[t]))}getFrameTimestamp(){let e;if(this.isActiveLive){let t=Math.floor(Math.random()*25)+5;e=Date.now()/1e3-t}else e=this.position||0;return Math.floor(e)}getUTC(){return Math.floor(Date.now()/1e3)}prepareUrl(e){return e.replace(`{@fts_fake_sec}`,String(this.getFrameTimestamp())).replace(`{@utc_sec}`,String(this.getUTC())).split(`&`).filter(e=>!/={@[a-zA-Z_]+}/.test(e)).join(`&`)}preparePixels(e){return this.validatePixels(this.params.pixels,{event:e,keys:[`url`]})}prepareHeartbeatPixels(){return this.validatePixels(this.params.pixels,{event:`heartbeat`,keys:[`url`,`interval`]})}call(e){try{this.idleCallbackIds.push(requestIdleCallback(()=>cr().src=this.prepareUrl(e)))}catch(e){this.log({message:e.message})}}send(e){let t=this.preparePixels(e);for(let{url:e}of t)this.call(e)}sendHeartbeat(e){this.call(e)}}export{E as VERSION,ut as ThinOneStat,l as SurfaceMode,u as SessionMode,d as SeekAction,o as Quality,lt as OneStat,lr as MediascopePixel,f as InteractiveInterfaceClick,s as ContentType,c as ConnectionType,a as ApiEnv};
6
+ var e=Object.defineProperty,t=e=>e;function n(e,n){this[e]=t.bind(null,n)}var r=(t,r)=>{for(var i in r)e(t,i,{get:r[i],enumerable:!0,configurable:!0,set:n.bind(r,i)})},i=[`adv`],a;(e=>{e.PROD=`prod`,e.VK_ALIAS=`vk_alias`,e.VIDEOTEST=`videotest`,e.TEST=`test`,e.OKCDN=`okcdn`,e.AUTO=`auto`})(a||={});var o;(e=>{e.Q144P=`mobile`,e.Q240P=`lowest`,e.Q360P=`low`,e.Q480P=`medium`,e.Q720P=`high`,e.Q1080P=`fullhd`,e.Q1440P=`quadhd`,e.Q2160P=`ultrahd`,e.UNKNOWN=`unknown`})(o||={});var s;(e=>{e.MP4=`mp4`,e.DASH=`dash`,e.DASH_SEP=`dash_sep`,e.ONDEMAND_DASH=`ondemand_dash`,e.HLS=`hls`,e.HLS_FMP4=`hls_fmp4`,e.ONDEMAND_HLS=`ondemand_hls`,e.WEBM=`webm`,e.AV1=`av1`,e.ONDEMAND_DASH_LIVE=`ondemand_dash_live`,e.ONDEMAND_HLS_LIVE=`ondemand_hls_live`,e.WEBRTC=`webrtc`,e.UNKNOWN=`unknown`,e.RTMP=`rtmp`})(s||={});var c;(e=>{e.HTTP1=`http1`,e.HTTP2=`http2`,e.HTTP3=`http3`})(c||={});var l;(e=>{e.PIP=`pip`,e.FULLSCREEN=`fullscreen`,e.EXTERNAL=`external`,e.PREFETCH=`prefetch`,e.AIRPLAY=`airplay`,e.CHROMECAST=`chromecast`,e.INVISIBLE=`invisible`,e.MINIMAL=`minimal`,e.SCREENCAST=`screencast`})(l||={});var u;(e=>{e.ADV=`adv`,e.COMMON=`common`})(u||={});var d;(e=>{e.SLIDER=`slider`,e.RICH=`rich`,e.DOUBLE_TAP=`double_tap`,e.TIME_CODE=`time_code`,e.EPISODE=`episode`,e.REWIND=`rewind`,e.LIVE=`live`,e.UNKNOWN=`unknown`})(d||={});var f;(e=>{e.GRAPH_SHOW=`iGraphShow`,e.GRAPH_HIDE=`iGraphHide`,e.NEXT_AREA=`iNextChapterArea`,e.NEXT_BUTTON=`iNextChapterBtn`,e.WATCH_AGAIN=`iWatchAgainBtn`})(f||={});var p={action_play:`ap`,action_play_interactive:`api`,first_bytes:`fb`,player_ready:`pr`,first_frame:`ff`,seek:`sk`,watch_coverage_record:`wr`,watch_coverage_live:`wl`,empty_buffer:`eb`,action_stop:`as`,close_at_empty_buffer:`cb`,content_error:`er`,player_interface_click:`pc`,next_movie:`nm`,track_switch:`ts`,failover:`fo`,quality:`qt`},m={vsid:`si`,isid:`is`,vid:`vi`,place:`pl`,quality:`qt`,cdn_host:`ch`,stat_type:`st`,param:`pm`,vk_app_id:`va`,track_code:`tc`,connection_type:`cnt`,connection_reused:`cr`,cached_data:`cd`,live:`lv`,muted:`mu`,mode:`mo`,subtitles:`sb`,failover:`fo`,download_speed:`ds`,manual_quality:`mq`,ref_domain:`rd`,direct_url:`du`},h={unknown:`un`,mobile:`m`,lowest:`ls`,low:`l`,medium:`md`,high:`h`,fullhd:`f`,quadhd:`q`,ultrahd:`u`},g={pip:`pi`,fullscreen:`fs`,external:`ex`,prefetch:`pr`,airplay:`ap`,chromecast:`cc`,invisible:`iv`,minimal:`minimal`,screencast:`screencast`};import{fillWithDefault as ee}from"@vkontakte/videoplayer-shared/evergreen";var _=512000749287,v=`CIOPGQJGDIHBABABA`,y={prod:`https://api.ok.ru`,vk_alias:`https://api.mycdn.me`,videotest:`https://videotestapi.ok.ru/api`,test:`https://apitest.ok.ru`,okcdn:`https://api.okcdn.ru`,auto:``},b={apiEnv:`vk_alias`,apiKey:v,apiApplicationId:_,apiBaseUrl:null,apiUvRestBaseUrl:`https://uvapi.okcdn.ru`,apiCollector:`ok.mobile.apps.video`,apiForticom:!0,apiUvRest:!1,requestRetryCount:1,useVsid64:!1,firstFlushTime:5e3,flushDebounceTime:1e4,flushMaxWait:6e4,storageExpiration:2160*60*1e3,watchCoverageInterval:15e3,disabledOperations:[`quality`],disabledCustomFields:[],shorten:!0,maxLoops:100,embedUrlParams:[],useBeacon:!0,synchronizeTime:!0,debugLog:!1,clearStorageAtUnload:!0,useVKComIsMobileLogic:!1,backoff:{start:1e3,factor:1.5,max:300*1e3,random:.1},useKeepalive:!1,watchCoverageTimeoutFix:!0,watchCoverageExactTime:!1,useStallContextForCloseAtEmptyBuffer:!0},x=e=>ee(e,b);function S(){let e=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`.split(``),t=Array(36),n=0,r,i;for(i=0;i<36;i++)i===8||i===13||i===18||i===23?t[i]=`-`:i===14?t[i]=`4`:(n<=2&&(n=33554432+Math.random()*16777216|0),r=n&15,n>>=4,t[i]=e[i===19?r&3|8:r]);return t.join(``)}import{assertNonNullable as te,combine as C,fromEvent as ne,isNonNullable as w,isNullable as re,merge as ie,now as T,observableFrom as ae,once as oe,safeStorage as se,Subject as ce,Subscription as le,ValueSubject as E,getWindow as ue}from"@vkontakte/videoplayer-shared/evergreen";import{Logger as de,detectEmbed as fe}from"@vkontakte/videoplayer-shared/evergreen";import{getWindow as pe}from"@vkontakte/videoplayer-shared/evergreen";var me=()=>Math.floor(Math.random()*2**32).toString(36).padStart(13,`0`),he=`0123456789abcdefghijklmnopqrstuvwxyz`,ge=`012`,_e=13,ve=()=>{let e=new Uint32Array(_e),{crypto:t}=pe();if(t?.getRandomValues)t.getRandomValues(e);else for(let t=0;t<_e;t++)e[t]=Math.floor(Math.random()*he.length);return Array.from(e,(e,t)=>{let n=t===0?ge:he;return n[e%n.length]}).join(``)};class D{static vsid;static getVSID(){return D.vsid}static generateVSID(e){return D.vsid=e?ve():me(),D.vsid}}var ye=e=>{let{operation:t,custom:n}=e,r=Object.fromEntries(Object.entries(n).map(([e,t])=>{let n=m[e]??e,r=t;return t&&(e===`mode`?r=g[t]??t:e===`quality`&&(r=h[t]??t)),[n,r]}));return{...e,operation:p[t]??t,custom:r}};import{getWindow as be}from"@vkontakte/videoplayer-shared/evergreen";var xe=async e=>{let t=be().crypto?.subtle;if(!t)return;let n=new TextEncoder().encode(e),r=await t.digest(`SHA-256`,n);return Array.from(new Uint8Array(r)).map(e=>e.toString(16).padStart(2,`0`)).join(``)},O=`1.0.120-beta.0`;import{ValueSubject as Se,getExponentialDelay as Ce,ErrorCategory as k,getWindow as we}from"@vkontakte/videoplayer-shared/evergreen";import{createTimers as Te,TimerModule as Ee}from"@vkontakte/videoplayer-shared/evergreen";var A=Te(Ee.STATISTICS),De=/Mobile|mini|Fennec|Android|iP(ad|od|hone)|opera (mini|mobi)/i;class Oe{id=`forticom`;params;authToken;sessionKey;authorizePromise;refreshAuthTokenPromise;consequentAuthErrors=0;authorized$=new Se(!1);backoffTimeoutId;constructor(e){this.params=e}async authorize(e){return this.authToken=e??await this.refreshAuthToken(),this._authorizeWithBackoff()}logBeacon(e){let t=this.createLogParams(e),n=this.sessionKey;n&&(this.params.apiTransport.sendBeacon(`log.externalLog`,t,n)||this.logRequest(e).catch(()=>{}))}async logRequest(e){let t=`log.externalLog`,n=this.createLogParams(e),r=this.sessionKey??await this._authorizeWithBackoff();if(!r)return;let i=async(e,a=this.params.config.requestRetryCount)=>{try{return await this.params.apiTransport.sendRequest(t,n,r)}catch(n){if(!n||!(`error_code`in n)){this.params.error$.next({id:`logRequestUnknown`,category:k.NETWORK,message:`Unknown ${t} error`,thrown:n});return}let r=n?.error_code;switch(r){case 102:case 103:case 104:return this.authorized$.next(!1),this.sessionKey=await this._authorizeWithBackoff(),a>0?i(e,a-1):void 0;case 401:return this.authorized$.next(!1),this.authToken=await this.refreshAuthToken(),this.sessionKey=await this._authorizeWithBackoff(),a>0?i(e,a-1):void 0;default:this.params.error$.next({id:`LogRequest#${r}`,category:k.EXTERNAL_API,message:`${t} error`,data:n});return}}};return i(e)}destroy(){A.clearTimeout(this.backoffTimeoutId),this.backoffTimeoutId=0}async refreshAuthToken(){if(this.params.refreshAuthToken)return this.refreshAuthTokenPromise||=this.params.refreshAuthToken().finally(()=>{this.refreshAuthTokenPromise=void 0}),this.refreshAuthTokenPromise}createLogParams(e){let t=`WEB`,n=!1,r=`@vkontakte/videoplayer-statistics:${O}`;if(this.params.config.useVKComIsMobileLogic){let{appVersion:e}=we().navigator;n=De.test(e)}else this.params.mobile&&(n=!0);return n&&(t=`M_WEB`),this.params.vktvVersion&&(t=`SMART_TV`,r=this.params.vktvVersion),{collector:this.params.config.apiCollector,data:JSON.stringify({application:r,platform:t,items:this.params.config.shorten?e.map(ye):e})}}_authorizeWithBackoff(){if(!this.consequentAuthErrors)return this._authorize();let e=Ce(this.consequentAuthErrors,this.params.config.backoff);return new Promise(t=>{this.backoffTimeoutId||=A.setTimeout(()=>{this._authorize().then(t).catch(e=>this.params.error$.next({id:`AuthorizeBackoff`,category:k.NETWORK,message:`Otherwise unhandled error in authorization`,thrown:e})).finally(()=>this.backoffTimeoutId=0)},e)})}async _authorize(){if(this.authorizePromise)return this.authorizePromise;this.sessionKey=void 0,this.authorized$.next(!1);let e={session_data:{version:2,device_id:this.params.deviceId,client_version:O.split(`-`)[0],client_type:`SDK_JS`}};return this.authToken&&(e.session_data.auth_token=this.authToken,e.session_data.version=3),this.authorizePromise=this.params.apiTransport.sendRequest(`auth.anonymLogin`,e).then(e=>((!e||!e.session_key)&&this.params.error$.next({id:`AuthorizeFailed`,category:k.EXTERNAL_API,message:`No session key`,data:e}),this.sessionKey=e?.session_key??void 0,this.sessionKey)).catch(async e=>{this.sessionKey=void 0;let t=e?.error_code;switch(t){case 401:return this.authToken=await this.refreshAuthToken(),this._authorizeWithBackoff()}t?this.params.error$.next({id:`Authorize#${t}`,category:k.EXTERNAL_API,message:`authorize error`,data:e}):this.params.error$.next({id:`AuthorizeUnknown`,category:k.NETWORK,message:`authorize error`,thrown:e})}).finally(()=>{this.authorizePromise=void 0,this.consequentAuthErrors=this.sessionKey?0:this.consequentAuthErrors+1,this.authorized$.next(this.sessionKey!==void 0)}),this.authorizePromise}}class ke{id=`uvrest`;authorized$=new Se(!1);params;statToken;authorizePromise;isDestroyed=!1;constructor(e){this.params=e}async authorize(){if(!this.isDestroyed)return this.authorizePromise?this.authorizePromise:(this.authorized$.next(!1),this.authorizePromise=this.executeAuthorize().finally(()=>{this.authorizePromise=void 0,this.isDestroyed||this.authorized$.next(this.statToken!==void 0)}),this.authorizePromise)}async logRequest(e){if(!e.length)return;let t=this.buildBody(e);try{let e=await this.getValidToken();if(!e){this.params.error$.next({id:`OneStat:UvRestApi:LogRequestNoToken`,category:k.NETWORK,message:`No stat token available for log request`});return}await this.params.apiTransport.sendJsonRequest(`/uv/stat/externalLog`,t,{Authorization:`Bearer ${e}`})}catch(e){await this.handleLogRequestError(e)&&await this.retryLogRequestAfterRecovery(t)}}logBeacon(e){if(!e.length)return;let t=this.statToken;if(!t)return;let n=this.buildBody(e);this.params.apiTransport.sendJsonBeacon(`/uv/stat/externalLog`,n,{Authorization:`Bearer ${t}`})}destroy(){this.isDestroyed=!0,this.authorizePromise=void 0,this.statToken=void 0,this.authorized$.next(!1)}async getValidToken(){return this.statToken?this.statToken:this.authorize()}async executeAuthorize(){try{let e=this.params.getAuthToken?await this.params.getAuthToken():await this.selfAuthorize();if(this.isDestroyed)return;if(!e){this.params.error$.next({id:`OneStat:UvRestApi:NoToken`,category:k.EXTERNAL_API,message:`No stat token obtained`});return}return this.statToken=e,this.statToken}catch(e){if(this.isDestroyed)return;this.params.error$.next({id:`OneStat:UvRestApi:AuthorizeFailed`,category:k.NETWORK,message:`Failed to get stat token`,thrown:e});return}}async selfAuthorize(){let e=this.params.timeSynchronisation?.now()??Date.now(),t=this.params.userId===void 0?``:String(this.params.userId),n=this.params.config.apiApplicationId,r=this.params.deviceId,i=await xe(`${t}${n}${r}${e}`);if(!i){this.params.error$.next({id:`OneStat:UvRestApi:SignatureUnavailable`,category:k.WTF,message:`crypto.subtle is unavailable for self authorization`});return}return(await this.params.apiTransport.sendJsonRequest(`/uv/stat/token`,{userId:t,applicationId:n,deviceId:r},{"X-Signature-Timestamp":String(e),Signature:i}))?.token}async handleLogRequestError(e){let t=e?.status;return t===401?(this.params.error$.next({id:`OneStat:UvRestApi:TokenExpired`,category:k.EXTERNAL_API,message:`Stat token expired (401), refreshing`,thrown:e}),this.authorized$.next(!1),this.statToken=void 0,await this.authorize()!==void 0):(this.params.error$.next({id:`OneStat:UvRestApi:LogRequestFailed#${t??`unknown`}`,category:k.EXTERNAL_API,message:`Log request failed with status ${t}`,thrown:e}),!1)}async retryLogRequestAfterRecovery(e){let t=this.statToken;if(!t){this.params.error$.next({id:`OneStat:UvRestApi:RetryNoToken`,category:k.NETWORK,message:`Cannot retry log request: no token after recovery`});return}try{await this.params.apiTransport.sendJsonRequest(`/uv/stat/externalLog`,e,{Authorization:`Bearer ${t}`})}catch(e){this.params.error$.next({id:`OneStat:UvRestApi:RetryFailed`,category:k.EXTERNAL_API,message:`Log request failed after token refresh`,thrown:e})}}buildBody(e){let t=`WEB`,n=!1,r=`@vkontakte/videoplayer-statistics:${O}`;if(this.params.config.useVKComIsMobileLogic){let{appVersion:e}=we().navigator;n=De.test(e)}else this.params.mobile&&(n=!0);return n&&(t=`M_WEB`),this.params.vktvVersion&&(t=`SMART_TV`,r=this.params.vktvVersion),{application:r,platform:t,user_id:this.params.userId,items:this.params.config.shorten?e.map(ye):e}}}import{ErrorCategory as j,now as M,getWindow as N}from"@vkontakte/videoplayer-shared/evergreen";class Ae{apiKey;apiBaseUrl;apiUvRestBaseUrl;apiEnv;timeSynchronisation;isApiBaseUrlFetched;params;constructor(e){this.params=e,this.apiKey=e.config.apiKey,this.apiEnv=e.config.apiEnv,this.apiBaseUrl=e.config.apiBaseUrl??y[this.apiEnv],this.apiUvRestBaseUrl=e.config.apiUvRestBaseUrl,this.timeSynchronisation=e.timeSynchronisation,this.isApiBaseUrlFetched=!1}async resolveApiBaseUrl(){if(this.apiEnv!==`auto`||this.isApiBaseUrlFetched)return this.apiBaseUrl;try{let e=atob(`aHR0cHM6Ly9kbnMuZ29vZ2xlL3Jlc29sdmU/bmFtZT12aWRlby5fZW5kcG9pbnQub2sucnUmdHlwZT1UWFQ=`),t=(await(await fetch(e,{method:`GET`,mode:`cors`,cache:`no-cache`})).json())?.Answer[0]?.data;if(!t)throw Error(`Wrong DNS response`);return t}catch(e){return this.params.error$.next({id:`OneStat:ApiTransport:resolveApiBaseUrl`,category:j.NETWORK,message:`Unhandled resolve api base url error`,thrown:e}),y.vk_alias}finally{this.isApiBaseUrlFetched=!0}}sendBeacon(e,t,n){if(!N().Blob||!N().navigator.sendBeacon)return!1;let r=this._prepareQueryParams({method:e,queryParams:t,sessionKey:n}),i=N().Blob,a=new i([r.toString()],{type:`application/x-www-form-urlencoded`});try{return N().navigator.sendBeacon(`${this.apiBaseUrl}/fb.do`,a)}catch(n){this.params.error$.next({id:`OneStat:ApiTransport:sendBeacon`,category:j.NETWORK,message:`Unhandled beacon error`,thrown:n,data:{method:e,params:t}})}return!1}async sendRequest(e,t,n){let r=M(),i=i=>{if(i instanceof TypeError&&[`Failed to fetch`,`NetworkError when attempting to fetch resource.`].includes(i.message)){this.params.error$.next({id:`Network`,category:j.NETWORK,message:`Request failed`,thrown:i});return}this.params.error$.next({id:`OneStat:ApiTransport:sendRequest`,category:j.NETWORK,message:`Unhandled request error`,thrown:i,data:{method:e,params:t,sessionKey:n,time:M()-r}})};this.apiBaseUrl=await this.resolveApiBaseUrl();let a={method:`post`,headers:{"Content-type":`application/x-www-form-urlencoded`},body:this._prepareQueryParams({method:e,queryParams:t,sessionKey:n})};return this.params.config.useKeepalive&&(a.keepalive=!0),fetch(`${this.apiBaseUrl}/fb.do`,a).then(e=>{let t=Number(e.headers.get(`content-length`))===0,n=new Date(e.headers.get(`date`)??``).getTime(),a=M()-r;if(isFinite(n)&&this.timeSynchronisation?.addServerTime(n,a),!t)return e.json().then(e=>Object.prototype.hasOwnProperty.call(e,`error_msg`)?Promise.reject(e):e,i)},i)}sendJsonBeacon(e,t,n){let r=`${this.apiUvRestBaseUrl}${e}`;try{fetch(r,{method:`POST`,headers:{"Content-Type":`application/json`,...n},body:JSON.stringify(t),keepalive:!0}).catch(t=>{this.params.error$.next({id:`OneStat:ApiTransport:sendJsonBeacon`,category:j.NETWORK,message:`Keepalive request failed`,thrown:t,data:{path:e}})})}catch(t){this.params.error$.next({id:`OneStat:ApiTransport:sendJsonBeacon`,category:j.NETWORK,message:`Failed to initiate keepalive request`,thrown:t,data:{path:e}})}}async sendJsonRequest(e,t,n){let r=M(),i=t=>{if(t instanceof TypeError&&[`Failed to fetch`,`NetworkError when attempting to fetch resource.`].includes(t.message)){this.params.error$.next({id:`Network`,category:j.NETWORK,message:`JSON request failed`,thrown:t});return}this.params.error$.next({id:`OneStat:ApiTransport:sendJsonRequest`,category:j.NETWORK,message:`Unhandled JSON request error`,thrown:t,data:{path:e,time:M()-r}})},a={method:`POST`,headers:{"Content-Type":`application/json`,...n},body:JSON.stringify(t)};return this.params.config.useKeepalive&&(a.keepalive=!0),fetch(`${this.apiUvRestBaseUrl}${e}`,a).then(e=>{let t=Number(e.headers.get(`content-length`))===0,n=new Date(e.headers.get(`date`)??``).getTime(),a=M()-r;if(isFinite(n)&&this.timeSynchronisation?.addServerTime(n,a),!e.ok)return Promise.reject({status:e.status,statusText:e.statusText});if(!t)return e.json().catch(i)},i)}_prepareQueryParams(e){let t=new URLSearchParams({format:`JSON`,method:e.method,application_key:this.apiKey});return e.sessionKey!==void 0&&t.append(`session_key`,e.sessionKey),Object.entries(e.queryParams).forEach(([e,n])=>t.append(e,typeof n==`string`?n:JSON.stringify(n))),t}}import{ErrorCategory as je,now as Me,safeStorage as P,debounceFn as Ne}from"@vkontakte/videoplayer-shared/evergreen";var Pe=e=>t=>t.timestamp+e>=Me();class Fe{params;error$;userSalt;isPaused=!1;loggerDebugLog;queues=[];constructor(e){this.params=e,this.error$=e.error$,this.userSalt=e.userSalt,this.loggerDebugLog=e.debugLogger.createComponentLog(`stat logger`);for(let t of e.apis)this.queues.push(this.createQueueState(t));this.housekeepStorage()}createQueueState(e){let t=`onestat_events_${e.id}`,n=Ne(()=>this.safeFlushQueue(a),this.params.config.firstFlushTime,{maxWait:this.params.config.firstFlushTime}),r=P.isPersistent()?this.params.config.flushMaxWait:0,i=Ne(()=>this.safeFlushQueue(a),this.params.config.flushDebounceTime,{maxWait:r}),a={api:e,storageKey:t,unsaltedStorage:[],lastVsid:void 0,firstFlush:n,debouncedFlush:i,subscription:e.authorized$.subscribe(e=>{e&&i()})};return a}isUrgent=e=>{let{operation:t}=e;return[`action_play`,`action_play_interactive`,`watch_coverage_record`,`watch_coverage_live`,`action_stop`,`close_at_empty_buffer`].includes(t)};safeFlushQueue(e){try{this.flushQueue(e)}catch(t){this.error$.next({id:`LoggerError`,category:je.WTF,message:t?String(t):`Unknown logger error`,thrown:t,data:{storageKey:e.storageKey}})}}readFromStorage(e){let t=P.get(e);try{return t?JSON.parse(t):{}}catch{}return{}}addToStorage(e,t){if(!this.userSalt){e.unsaltedStorage.push(t);return}let n=this.readFromStorage(e.storageKey),r=(n[this.userSalt]??[]).filter(Pe(this.params.config.storageExpiration));P.set(e.storageKey,JSON.stringify({...n,[this.userSalt]:[...r,t]}))}getFromStorage(e){return this.userSalt?(this.readFromStorage(e.storageKey)[this.userSalt]??[]).filter(Pe(this.params.config.storageExpiration)):e.unsaltedStorage}markStorageSent(e){if(!this.userSalt){e.unsaltedStorage=[];return}let t=this.readFromStorage(e.storageKey);delete t[this.userSalt],P.set(e.storageKey,JSON.stringify(t))}housekeepStorage(){for(let e of this.queues){let t=this.readFromStorage(e.storageKey);for(let[n,r]of Object.entries(t)){let i=r.filter(Pe(this.params.config.storageExpiration));i.length?t[n]=i:delete t[n],this.loggerDebugLog({message:`${r.length} retrieved from storage, ${i.length} of them actual (${e.storageKey})`})}P.set(e.storageKey,JSON.stringify(t))}}log(e){for(let t of this.queues)this.addToStorage(t,e),!this.isPaused&&(this.isUrgent(e)?this.flushQueue(t):t.lastVsid===e.custom.vsid?t.debouncedFlush():t.firstFlush(),t.lastVsid=e.custom.vsid)}flush(e={wantBeacon:!1,clearStorage:!0}){for(let t of this.queues)this.flushQueue(t,e)}flushQueue(e,{wantBeacon:t,clearStorage:n}={wantBeacon:!1,clearStorage:!0}){let r=this.getFromStorage(e);r.length!==0&&(e.api.authorized$.getValue()?(t&&this.params.config.useBeacon?(this.loggerDebugLog({message:`Flushing ${r.length} events through beacon (${e.storageKey})`}),e.api.logBeacon(r)):(this.loggerDebugLog({message:`Flushing ${r.length} events (${e.storageKey})`}),e.api.logRequest(r)),n&&this.markStorageSent(e)):e.api.authorize(),e.firstFlush.cancel(),e.debouncedFlush.cancel())}pause(){this.isPaused=!0;for(let e of this.queues)e.debouncedFlush.cancel()}resume(){this.isPaused=!1;for(let e of this.queues)e.debouncedFlush()}destroy(){for(let e of this.queues)e.subscription.unsubscribe(),e.firstFlush.cancel(),e.debouncedFlush.cancel();this.queues=[]}}import{HttpConnectionType as Ie,Surface as F,VideoFormat as I,VideoQuality as L}from"@vkontakte/videoplayer-core/evergreen";import{assertNever as Le}from"@vkontakte/videoplayer-shared/evergreen";var Re=e=>e&&{[L.INVARIANT]:`unknown`,[L.Q_144P]:`mobile`,[L.Q_240P]:`lowest`,[L.Q_360P]:`low`,[L.Q_480P]:`medium`,[L.Q_576P]:`unknown`,[L.Q_720P]:`high`,[L.Q_1080P]:`fullhd`,[L.Q_1440P]:`quadhd`,[L.Q_2160P]:`ultrahd`,[L.Q_4320P]:`unknown`}[e],ze=e=>e&&{[Ie.HTTP1]:`http1`,[Ie.HTTP2]:`http2`,[Ie.QUIC]:`http3`}[e],Be=e=>{if(e!==void 0)switch(e){case I.MPEG:return`mp4`;case I.DASH:case I.DASH_LIVE:case I.DASH_STREAMS:return`dash`;case I.DASH_SEP:return`dash_sep`;case I.DASH_ONDEMAND:return`ondemand_dash`;case I.DASH_WEBM:case I.DASH_LIVE_WEBM:return`webm`;case I.DASH_WEBM_AV1:return`av1`;case I.DASH_LIVE_CMAF:return`ondemand_dash_live`;case I.HLS:case I.HLS_LIVE:return`hls`;case I.HLS_FMP4:return`hls_fmp4`;case I.HLS_ONDEMAND:return`ondemand_hls`;case I.HLS_LIVE_CMAF:return`ondemand_hls_live`;case I.WEB_RTC_LIVE:return`webrtc`;default:return Le(e)}},Ve=(e,t)=>{if(t)return`minimal`;switch(e){case void 0:case F.NONE:case F.INLINE:return;case F.FULLSCREEN:return`fullscreen`;case F.SECOND_SCREEN:return`chromecast`;case F.PIP:return`pip`;case F.INVISIBLE:return`invisible`;default:return Le(e)}},He=e=>{switch(e){case`slow-2g`:return`poor`;case`2g`:return`poor`;case`3g`:return`good`;case`4g`:return`excellent`}};class Ue{offset=void 0;constructor(e){this.offset=e}getOffset(){return this.offset??0}setOffset(e){this.offset=e}now(){return Date.now()+(this.offset??0)}date(e=new Date){return e.setTime(e.getTime()+(this.offset??0)),e}addServerTime(e,t){let n=e-Date.now()-t/2;if(Math.abs(n)<1e3){this.offset=0;return}if(this.offset===void 0)this.offset=Math.round(n);else{let e=.2;this.offset=Math.round(e*n+(1-e)*this.offset)}}}import{PlaybackState as We}from"@vkontakte/videoplayer-core/evergreen";import{filter as Ge,filterChanged as Ke,fromEvent as qe,isNullable as Je,map as Ye,merge as R,Observable as Xe,Subject as Ze,Subscription as Qe,ValueSubject as $e}from"@vkontakte/videoplayer-shared/evergreen";var et=(e,t)=>new Xe(n=>{let r=new Qe,i=R(qe(window,`beforeunload`),e.events.willDestruct$),a=new $e(void 0),o;r.add(e.info.isLive$.pipe(Ke()).subscribe(t=>{o&&(o.unsubscribe(),a.next(void 0)),o=t?e.info.liveTime$.pipe(Ye(e=>e&&e/1e3)).subscribe(a):e.info.position$.subscribe(a),r.add(o)}));let{playing$:s,paused$:c,looped$:l}=e.events,u=e.events.willSeek$.pipe(Ge(()=>e.info.playbackState$.getValue()===We.PLAYING)),d=e.events.seeked$.pipe(Ge(()=>e.info.playbackState$.getValue()===We.PLAYING)),f=!1,p=new Ze;r.add(u.subscribe(()=>{f||p.next(),f=!0})),r.add(d.subscribe(()=>f=!1));let m=new Ze,h=new Ze,g=()=>t.watchCoverageExactTime?e.info.isLive$.getValue()?e.getExactLiveTime():e.getExactTime():a.getValue()??0,ee=e.info.stallStartTime$.pipe(Ke(),Ge(e=>e>0)),_=R(s,d,m).pipe(Ye(()=>g())),v=R(l,R(c,p,i,h,ee).pipe(Ye(()=>g()))),y,b=e=>{y=e},x=e=>{let t=e;Je(y)||y===t||Je(t)||(n.next({from:y,to:t}),y=void 0)},S=()=>{h.next(),m.next()};if(r.add(_.subscribe(b)),r.add(v.subscribe(x)),t.forceInterval&&isFinite(t.forceInterval))if(t.watchCoverageTimeoutFix){let e,n=()=>{e!==void 0&&(A.clearTimeout(e),e=void 0)};r.add(_.subscribe(()=>{n(),e=A.setTimeout(S,t.forceInterval)})),r.add(v.subscribe(()=>n())),r.add(()=>n())}else{let e=0;r.add(_.subscribe(()=>e=A.setTimeout(S,t.forceInterval))),r.add(v.subscribe(()=>A.clearTimeout(e)))}return r}),tt=new Set(i),nt=e=>tt.has(e);import{Surface as z}from"@vkontakte/videoplayer-core/evergreen";import{debounce as rt,filterChanged as it,Subject as at}from"@vkontakte/videoplayer-shared/evergreen";var ot=({uiEvents:e,initExternalSubscribers:t,subscribe:n,player:r})=>{let i=new at;t(i.pipe(rt(0),it()));let a=!1,o=!1,s=!1;n(e.inPiP$,e=>{a=e,i.next(e?z.PIP:o?z.FULLSCREEN:s?z.INLINE:void 0)}),n(e.inFullscreen$,e=>{o=e,i.next(e?z.FULLSCREEN:a?z.PIP:s?z.INLINE:void 0)}),r&&n(r.info.surface$,e=>{switch(e){case z.SECOND_SCREEN:case z.INVISIBLE:case z.NONE:case z.INLINE:!o&&!a&&(e===z.INLINE&&(s=!0),i.next(e));break}})},st=`_one-stat_deviceId`,ct=(e,t)=>e&&t?`${e}_${t}`:e??t,lt=({width:e,height:t})=>e&&t?`${Math.round(e)}x${Math.round(t)}`:void 0,ut=()=>{let e=new le;return{subscription:e,subscribe:(t,n)=>{t&&e.add(t.subscribe(n))}}};class dt{apis;logger;config;subscription=new le;beforeunloadSubscription;timeSynchronisation;debugLogger=new de;oneStatDebugLog=this.debugLogger.createComponentLog(`onestat`);loopCounter=0;disabled=!1;sessionMode=`common`;surfaceMode;experimental;get vsid$(){return new E(D.getVSID())}isid$=new E(void 0);zenUid$=new E(void 0);statContext;isEmbed;embedParent;uiEvents;player;seekAction$=new E(`unknown`);deviceId;playerSize={width:0,height:0};constructor(e,t){this.statContext=e,this.config=x(t.config??{}),w(t.apiEnv)&&(this.config.apiEnv=t.apiEnv),this.config.synchronizeTime&&(this.timeSynchronisation=new Ue);let n=new ce;this.experimental={error$:n};let r=se.get(st);this.statContext.deviceId?this.deviceId=this.statContext.deviceId:r?this.deviceId=r:(this.deviceId=S(),se.set(st,this.deviceId)),this.resetViewSession(),t.useIsid&&this.isid$.next(t.isid??ve()),this.zenUid$.next(t.zenUid);let i=new Ae({config:this.config,error$:n,timeSynchronisation:this.timeSynchronisation}),a=[];this.config.apiForticom&&a.push(new Oe({config:this.config,apiTransport:i,refreshAuthToken:t.refreshAuthToken,mobile:this.statContext.mobile??!1,deviceId:this.deviceId,error$:n,vktvVersion:t.vktvVersion})),this.config.apiUvRest&&a.push(new ke({config:this.config,apiTransport:i,error$:n,userId:this.statContext.userId,deviceId:this.deviceId,getAuthToken:t.getUvRestAuthToken,mobile:this.statContext.mobile??!1,vktvVersion:t.vktvVersion,timeSynchronisation:this.timeSynchronisation})),this.apis=a,this.logger=new Fe({config:this.config,debugLogger:this.debugLogger,apis:this.apis,error$:n,userSalt:t.userSalt});let{isEmbed:o,topOrigin:s}=fe();this.isEmbed=o,this.embedParent=s?new URL(s).hostname:void 0,this.subscribe()}updateContext(e){this.statContext={...this.statContext,...e}}attachTo(e){let t=new le,n=(e,n)=>t.add(e.subscribe(n)),r,i,a,o=!1,s=!1,c=()=>{o||(o=!0,w(r)&&(r={startedAt:T(),liveAtStall:!!this.statContext.liveEdge}))};n(e.events.willStart$,()=>{let t=e.info.position$.getValue();this.logActionPlay({position:t}),this.statContext.projectId&&this.logActionPlayInteractive()}),n(e.events.looped$,()=>{this.resetViewSession(),++this.loopCounter>this.config.maxLoops&&(this.disabled=!0),this.logActionPlay({position:e.info.position$.getValue()})}),n(e.events.willSeek$,()=>{w(r)&&o&&this.logEmptyBuffer({duration:T()-r.startedAt,liveAtStall:r.liveAtStall}),r=void 0,s=!0}),n(e.events.seeked$,()=>{!s&&w(r)&&o&&(this.logEmptyBuffer({duration:T()-r.startedAt,liveAtStall:r.liveAtStall}),r=void 0),s=!1;let t=this.seekAction$.getValue();this.logSeek({action:t===`rich`?`slider`:t,time:e.info.position$.getValue()}),this.seekAction$.next(`unknown`)}),n(e.events.paused$,()=>{this.logPause({position:e.info.position$.getValue()})}),n(e.events.willResume$,()=>{this.logPlay()}),n(e.events.started$,()=>{this.statContext.clip&&this.logWatchCoverageRecord({start:0,end:0})}),n(e.events.playing$,()=>{this.logPlaying()}),n(et(e,{forceInterval:this.config.watchCoverageInterval,watchCoverageTimeoutFix:this.config.watchCoverageTimeoutFix,watchCoverageExactTime:this.config.watchCoverageExactTime}),t=>{let n=e.info.isLive$.getValue(),r=e.info.atLiveEdge$.getValue(),i={start:parseFloat(t.from.toFixed(3)),end:parseFloat(t.to.toFixed(3))};if(r){let t=e.info.liveLatency$.getValue()??0,n=e.info.currentVideoBuffer$.getValue()?.end??0,r=e.info.liveBufferTime$.getValue()??0,a=Math.round((t-(n-r))*1e3),o=Math.round(t*1e3);a&&o&&(i.latency=a,i.bufferLatency=o)}n?this.logWatchCoverageLive(i):this.logWatchCoverageRecord(i)}),n(e.info.stallStartTime$,e=>{e>0?r={startedAt:e,liveAtStall:!!this.statContext.liveEdge}:(w(r)&&o&&this.logEmptyBuffer({duration:Math.round(T()-r.startedAt),liveAtStall:r.liveAtStall}),r=void 0)});let l=!1;t.add(e.events.fatalError$.pipe(oe()).subscribe(()=>l=!0)),n(e.events.willStop$,()=>{(this.config.useStallContextForCloseAtEmptyBuffer?w(r)&&o:e.info.isBuffering$.getValue()&&o)?(this.logCloseAtEmptyBuffer({duration:w(r)?Math.round(T()-r.startedAt):0,liveAtStall:r?.liveAtStall}),r=void 0):l||this.logActionStop()}),n(e.events.managedError$,({id:e})=>{this.logError({fatal:!1,errorType:e})}),n(e.events.fatalError$,({id:e})=>{this.logError({fatal:!0,errorType:e})}),n(e.events.firstBytes$,e=>{i=T(),this.logFirstBytes({time:e})}),n(e.events.willStart$,()=>a=T()),n(e.info.currentVideoBuffer$,e=>{!o&&e&&e.end-e.start>0&&w(i)&&(this.logPlayerReady({duration:T()-i}),c())}),n(e.events.firstFrame$,()=>{w(i)&&!o&&(this.logPlayerReady({duration:T()-i}),c()),w(a)&&this.logFirstFrame({time:T()-a})});let u;n(e.info.currentVideoStream$,e=>{e&&(this.updateContext({videoCodec:e.codec}),u&&e.id!==u&&this.logTrackSwitch(e),u=e.id)});let d;n(e.info.currentAudioStream$,e=>{e&&(this.updateContext({audioCodec:e.codec}),d&&e.id!==d&&this.logTrackSwitch(e),d=e.id)}),n(e.info.atLiveEdge$,e=>this.updateContext({liveEdge:e})),n(C({muted:e.info.muted$,volume:e.info.volume$}),({muted:e,volume:t})=>this.updateContext({audible:!e&&t>0})),n(e.info.currentQuality$,e=>{let t=Re(e);this.updateContext({quality:t}),t&&this.logQuality(t)}),n(e.info.isAutoQualityEnabled$,e=>this.updateContext({autoQuality:e})),n(e.info.currentFormat$,e=>this.updateContext({contentType:Be(e)})),n(e.info.currentPlaybackRate$,e=>this.updateContext({rate:e}));let f;return n(e.info.hostname$,e=>{let t=f!==void 0&&f!==e;this.updateContext({cdnHostname:e,failover:t}),t&&this.logFailover(e),f=e}),n(e.info.throughputEstimation$,e=>this.updateContext({downloadSpeed:e})),n(e.info.httpConnectionType$,e=>{this.statContext.firstConnectionType||this.updateContext({firstConnectionType:ze(e)}),this.updateContext({connectionType:ze(e)})}),n(e.info.httpConnectionReused$,e=>{re(this.statContext.firstConnectionReused)&&this.updateContext({firstConnectionReused:e}),this.updateContext({connectionReused:e})}),n(e.info.surface$,e=>{this.uiEvents||(this.surfaceMode=Ve(e,this.statContext.isPreviewPlayerView))}),n(C({current:e.info.currentTextTrack$,available:e.info.availableTextTracks$}),({current:e,available:t})=>{let n=t.find(({id:t})=>t===e),r=n&&(n.isAuto?`${n.language}_auto`:n.language);this.updateContext({subtitles:r?.split(`.`)[0]})}),this.player=e,this.uiEvents&&this.attachToUi(this.uiEvents),this.resubscribeBeforeunload(),this.subscription.add(t),t}attachToUi(e){this.uiEvents=e;let{subscription:t,subscribe:n}=ut();return this.player?(n(e.actionRewind$,()=>{this.player&&(this.resetViewSession(),this.logActionPlay({position:this.player.info.position$.getValue()}))}),n(e.actionSeek$,this.seekAction$),ot({uiEvents:this.uiEvents,initExternalSubscribers:e=>{n(e,e=>{this.surfaceMode=Ve(e,this.statContext.isPreviewPlayerView)})},subscribe:n,player:this.player}),n(e.actionSetSubtitle$,e=>this.updateContext({subtitles:e?.split(`.`)[0]})),n(e.nextMovie$,e=>this.logNextMovie(e)),n(e.playerSize$,({width:e,height:t})=>{e&&t&&(this.playerSize={width:e,height:t})}),n(e.fatalError$,e=>e&&this.logError({fatal:!0,errorType:e.type})),this.resubscribeBeforeunload(),this.subscription.add(t),t):t}attachToAds(e){let{subscription:t,subscribe:n}=ut();return n(e.slotRequested$,()=>this.logAdSlotRequest()),n(e.started$,e=>this.logAdStarted(e)),n(e.paused$,()=>this.logAdPaused()),n(e.resumed$,()=>this.logAdResumed()),n(e.ended$,()=>this.logAdEnded()),n(e.skipped$,()=>this.logAdSkipped()),n(e.clicked$,()=>this.logAdClicked()),n(e.error$,e=>this.logError({errorType:e,fatal:!1})),this.resubscribeBeforeunload(),this.subscription.add(t),t}attachToInteractive(e){let{subscription:t,subscribe:n}=ut();return n(e.click$,e=>this.logInterfaceClick(e)),n(e.nextMovie$,e=>this.logNextMovie(e)),this.resubscribeBeforeunload(),this.subscription.add(t),t}authorize(e){return Promise.all(this.apis.map(t=>t.authorize(e)))}setSessionMode(e){this.sessionMode=e,this.oneStatDebugLog({message:`session mode set to ${e}`})}pause(){this.logger.pause(),this.oneStatDebugLog({message:`paused`})}resume(){this.logger.resume(),this.oneStatDebugLog({message:`resumed`})}destroy(){this.logger.flush(),this.subscription.unsubscribe(),this.apis.forEach(e=>e.destroy()),this.logger.destroy()}resetViewSession(){this.oneStatDebugLog({message:`VSID reset`}),D.generateVSID(this.config.useVsid64)}getDeviceId(){return this.deviceId}setFieldBlacklist(){}logInited(){}logReady(e){this.log({operation:`player_ready`,param:String(e.time)})}logStarted(...e){this.logActionPlay(...e)}logPlay(){this.log({operation:`play_toggle`})}logPlaying(){this.log({operation:`playing`})}logPause(e){this.log({operation:`pause`,param:String(Math.round(e.position))})}logSeek(e){this.log({operation:`seek`,param:e.action,time:e.time})}logFirstBytes(e){this.log({operation:`first_bytes`,param:String(e.time)})}logFirstFrame(e){this.log({operation:`first_frame`,param:String(e.time)})}logError(e){this.log({operation:`content_error`,param:`${e.fatal?`fatal`:`recoverable`}_${e.errorType}`})}logWatchCoverageRecord(e){this.log({operation:`watch_coverage_record`,param:`${e.start}-${e.end}`},{in_history:this.statContext.inHistory?1:void 0})}logWatchCoverageLive(e){let t=this.timeSynchronisation?.getOffset()??0,n=e.start+t,r=e.end+t;this.log({operation:`watch_coverage_live`,param:`${n}-${r}`},{in_history:this.statContext.inHistory?1:void 0,latency:e.latency,buffer_latency:e.bufferLatency})}logEmptyBuffer(e){this.log({operation:`empty_buffer`,param:String(e.duration)},w(e.liveAtStall)?{live:e.liveAtStall?1:void 0}:{})}logDownloadSpeed(){}logAdSlotRequest(){this.log({operation:`adv`,param:`slot_request`})}logAdStarted(e){this.log({operation:`adv`,param:e})}logAdPaused(){this.log({operation:`adv`,param:`pause`})}logAdResumed(){this.log({operation:`adv`,param:`resume`})}logAdEnded(){this.log({operation:`adv`,param:`ended`})}logAdSkipped(){this.log({operation:`adv`,param:`skip`})}logAdClicked(){this.log({operation:`adv`,param:`click`})}logInterfaceClick(e){this.log({operation:`player_interface_click`,param:e})}logNextMovie(e){this.log({operation:`next_movie`,param:String(e)})}subscribe(){this.resubscribeBeforeunload();let e=ue().navigator.connection;e&&`onchange`in e&&`effectiveType`in e&&this.subscription.add(ie(ne(e,`change`),ae([`init`])).subscribe(()=>this.updateContext({network:He(e.effectiveType)}))),this.config.debugLog&&this.debugLogger.log$.subscribe(e=>{console.debug(`%c stat `,`background:#fa6470;`,`component: ${e.component}.`,e.message)})}resubscribeBeforeunload(){this.beforeunloadSubscription?.unsubscribe(),this.beforeunloadSubscription=ne(window,`beforeunload`).subscribe(()=>this.logger.flush({wantBeacon:!0,clearStorage:this.config.clearStorageAtUnload})),this.subscription.add(this.beforeunloadSubscription)}logPlayerReady(e){this.log({operation:`player_ready`,param:String(Math.round(e.duration))})}logActionPlay(e){this.log({operation:`action_play`,param:String(Math.round(e.position))})}logActionPlayInteractive(){this.statContext.projectId&&this.log({operation:`action_play_interactive`,param:String(this.statContext.movieId)},{vid:this.statContext.projectId})}logFailover(e){this.log({operation:`failover`,param:e})}logCloseAtEmptyBuffer(e){this.log({operation:`close_at_empty_buffer`,param:String(Math.round(e.duration))},w(e.liveAtStall)?{live:e.liveAtStall?1:void 0}:{})}logActionStop(){this.log({operation:`action_stop`})}logTrackSwitch(e){this.log({operation:`track_switch`,param:e.id})}logQuality(e){this.log({operation:`quality`,param:e})}log(e,t={}){if(this.disabled){this.oneStatDebugLog({message:`operation ${e.operation} but statistics is disabled`});return}if(this.sessionMode===`adv`&&!nt(e.operation)){this.oneStatDebugLog({message:`operation ${e.operation} skipped: adv mode`});return}if(this.config.disabledOperations.includes(e.operation)){this.oneStatDebugLog({message:`operation ${e.operation} but it is disabled`});return}this.oneStatDebugLog({message:`operation ${e.operation} ${e.param}`});let n=this.createLogItem(e,t);this.logger.log(n)}createLogItem({operation:e,param:t,time:n},r={}){let i=this.timeSynchronisation?.now()??T(),a=this.vsid$.getValue();te(a);let o=this.isid$.getValue(),s=e===`empty_buffer`||e===`close_at_empty_buffer`?this.statContext.connectionType:this.statContext.firstConnectionType,c=this.statContext.firstConnectionReused,l=this.player?.info.rttEstimation$.getValue(),u,d;if(this.isEmbed||this.statContext.place===`embed`){u=this.embedParent;let e=[...new URLSearchParams(location.search).entries()].filter(([e,t])=>this.config.embedUrlParams.includes(e));d=new URLSearchParams(e).toString()}else if(this.statContext.place===`direct`){let e=this.statContext.refDomain||document.referrer;u=e&&URL.canParse(e)?new URL(e).hostname:e,d=location.href.substring(0,1024)}let f={vsid:a,isid:o,vid:this.statContext.movieId,uid:this.zenUid$.getValue(),ct:this.statContext.contentType,place:this.isEmbed?`embed`:this.statContext.place,quality:this.statContext.quality,cdn_host:this.statContext.cdnHostname,stat_type:this.statContext.autoplay===!0?`auto`:this.statContext.autoplay===!1?``:void 0,param:t,vk_app_id:this.statContext.vkAppId,track_code:this.statContext.trackCode,connection_type:s,connection_reused:c===!0?1:c===!1?0:void 0,cached_data:this.statContext.cached===!0?1:this.statContext.cached===!1?0:void 0,live:this.statContext.liveEdge?1:void 0,muted:this.statContext.audible===!1?1:void 0,mode:this.surfaceMode??this.statContext.mode,subtitles:this.statContext.subtitles,download_speed:this.statContext.downloadSpeed,manual_quality:this.statContext.autoQuality?void 0:1,ref_domain:u,direct_url:d,rate:this.statContext.rate===1||re(this.statContext.rate)?void 0:this.statContext.rate.toFixed(2),view_360:this.statContext.is3d?1:void 0,codec_info:ct(this.statContext.videoCodec,this.statContext.audioCodec),aid:this.statContext.albumId,vk_playlist_id:this.statContext.vkPlaylistId,vport:lt(this.statContext.playerSize??this.playerSize),rtt:w(l)?Math.round(l):void 0,bw:w(this.statContext.downloadSpeed)?Math.round(this.statContext.downloadSpeed):void 0,audio_player_type:this.statContext.audioPlayerType,has_subscription:this.statContext.hasSubscription,is_downloaded:this.statContext.isDownloaded,is_url_in_cache:this.statContext.isUrlInCache,like:this.statContext.like,track_type:this.statContext.trackType,...r};for(let e of this.config.disabledCustomFields)delete f[e];return{operation:e,type:1,time:n,network:this.statContext.network,timestamp:i,custom:f}}}var ft={};r(ft,{VERSION:()=>O,ThinOneStat:()=>hr,SessionMode:()=>Mt,ActionSeekType:()=>Nt});import{assertNonNullable as pt,detectEmbed as mt,fromEvent as ht,Logger as gt,merge as _t,combine as B,filter as V,filterChanged as vt,isNullable as yt,once as bt,skip as xt,debounce as St,safeStorage as Ct,getWindow as wt,Subject as Tt,Subscription as H,ValueSubject as Et,ErrorCategory as U}from"@vkontakte/videoplayer-shared/evergreen";import{clientChecker as W}from"@vkontakte/videoplayer-core/evergreen";import{fillWithDefault as Dt}from"@vkontakte/videoplayer-shared/evergreen";var Ot=[`adv_configuration`,`adv_request`,`adv_break_started`,`adv_break_ended`,`adv_error`],kt=[`stop`,`error`,`cannot_play`],At=[`start_session`,`play`,`manifest_request`,`first_byte_manifest`,`first_media_request`,`first_byte_media`,`first_video_frame_decoded`,`ready`,`playback_started`,`watched_n`],jt;(e=>{e.AUTO=`auto`,e.AUTO_POOR=`auto_poor`,e.AUTO_RICH=`auto_rich`})(jt||={});var Mt;(e=>{e.ADV=`adv`,e.COMMON=`common`})(Mt||={});var Nt;(e=>{e.SLIDER=`slider`,e.RICH=`rich`,e.DOUBLE_TAP=`double_tap`,e.TIME_CODE=`time_code`,e.EPISODE=`episode`,e.REWIND=`rewind`,e.LIVE=`live`,e.UNKNOWN=`unknown`})(Nt||={});var Pt=512000749287,Ft=`CIOPGQJGDIHBABABA`,It={prod:`https://api.ok.ru`,vk_alias:`https://api.mycdn.me`,videotest:`https://videotestapi.ok.ru/api`,test:`https://apitest.ok.ru`,okcdn:`https://api.okcdn.ru`,auto:``},Lt={apiKey:Ft,apiApplicationId:Pt,apiEnv:`okcdn`,apiBaseUrl:null,apiUvRestBaseUrl:`https://uvapi.okcdn.ru`,apiForticom:!0,apiUvRest:!1,requestRetryCount:3,useVsid64:!1,flushFirstTime:5e3,flushDebounceTime:1e4,flushMaxWait:6e4,flushRetryFirstDelay:3e3,flushRetryMaxDelay:18e5,storageExpiration:2160*60*1e3,clearStorageAtUnload:!1,disabledEvents:[],disabledParams:[],synchronizeTime:!0,useBeacon:!1,alwaysSendDesktop:!1,watchCoverageHeartbeatInterval:3e4,watchCoverageExactTime:!1,watchedNDefaultTargetDuration:1e3,telemetryInterval:3e4,downloadBytesMaxTime:3e4,maxLoops:100},Rt=e=>Dt(e,Lt);import{ErrorCategory as G,ValueSubject as zt}from"@vkontakte/videoplayer-shared/evergreen";function Bt(e){let t=Error(`HTTP ${e.status}: ${e.statusText}`);return t.status=e.status,t.statusText=e.statusText,t.response=e,t}function Vt(){let e=Error(`Request timeout`);return e.isTimeout=!0,e}function Ht(e){let t=Error(e.error_msg);return t.errorData=e,t}function Ut(e){if(!(!e||typeof e!=`object`)){if(`errorData`in e)return e.errorData?.error_code;if(`error_code`in e){let t=e.error_code;return typeof t==`number`?t:void 0}}}var Wt=e=>e.match(/^\d+\.\d+\.\d+/)?.[0]??e,Gt=({application:e,platform:t,product:n,...r})=>r;class Kt{id=`forticom`;authorized$;params;authToken;sessionKey;authorizePromise;refreshAuthTokenPromise;isDestroyed=!1;constructor(e){this.params=e,this.authorized$=new zt(!1)}async authorize(e){return this.authToken=e??await this.refreshAuthToken(),this.doAuthorize()}logBeacon(e){if(!e.length)return;let t=this.createLogParams(e),n=this.sessionKey;n&&(this.params.apiTransport.sendBeacon(`log.logUvStat`,t,n)||this.logRequest(e).catch(()=>{}))}async logRequest(e){if(!e.length)return;let t=`log.logUvStat`,n=this.createLogParams(e),r={};try{let e=await this.getValidSessionKey();if(!e){this.params.errors$.next({id:`ThinOneStat:Api:LogRequestNoSession`,category:G.NETWORK,message:`No session key available for log request`});return}await this.params.apiTransport.sendRequest(t,n,e,r)}catch(e){await this.handleLogRequestError(e,t,n)&&await this.retryLogRequestAfterSessionRecovery(t,n)}}async getValidSessionKey(){return this.sessionKey?this.sessionKey:this.doAuthorize()}async handleLogRequestError(e,t,n){let r=Ut(e);return r===void 0?(this.params.errors$.next({id:`ThinOneStat:Api:LogRequestUnknown`,category:G.NETWORK,message:`Unknown ${t} error`,thrown:e,data:{params:n}}),!1):this.isSessionExpiredError(r)?(this.params.errors$.next({id:`ThinOneStat:Api:LogRequestSessionError#${r}`,category:G.EXTERNAL_API,message:`Session error: ${r}`,data:{error:e,code:r}}),this.authorized$.next(!1),this.sessionKey=void 0,await this.doAuthorize()!==void 0):r===401?(this.params.errors$.next({id:`ThinOneStat:Api:LogRequestAuthLogin#${r}`,category:G.EXTERNAL_API,message:`Auth token expired`,data:{error:e,code:r}}),this.authorized$.next(!1),this.authToken=await this.refreshAuthToken(),this.sessionKey=void 0,await this.doAuthorize()!==void 0):(this.params.errors$.next({id:`ThinOneStat:Api:LogRequest#${r}`,category:G.EXTERNAL_API,message:`${t} error`,data:{error:e,code:r,params:n}}),!1)}async retryLogRequestAfterSessionRecovery(e,t){let n=this.sessionKey;if(!n){this.params.errors$.next({id:`ThinOneStat:Api:LogRequestRetryFailed`,category:G.NETWORK,message:`Cannot retry log request: no session key after recovery`});return}try{await this.params.apiTransport.sendRequest(e,t,n)}catch(e){let t=Ut(e);this.params.errors$.next({id:`ThinOneStat:Api:LogRequestRetryFailed#${t??`unknown`}`,category:G.EXTERNAL_API,message:`Log request failed after session recovery`,thrown:e,data:{originalError:e,retryCode:t}})}}isSessionExpiredError(e){return!!e&&[102,103,104].includes(e)}async doAuthorize(e=1){if(!this.isDestroyed)return this.authorizePromise?this.authorizePromise:(this.sessionKey=void 0,this.authorized$.next(!1),this.authorizePromise=this.executeAuthorize(e).finally(()=>{this.authorizePromise=void 0,this.isDestroyed||this.authorized$.next(this.sessionKey!==void 0)}),this.authorizePromise)}async executeAuthorize(e){let t=this.buildAuthParams();try{let e=await this.params.apiTransport.sendRequest(`auth.anonymLogin`,t);if(this.isDestroyed)return;if(!e?.session_key){this.params.errors$.next({id:`ThinOneStat:Api:AuthorizeFailed`,category:G.EXTERNAL_API,message:`No session key in response`,data:e});return}return this.sessionKey=e.session_key,this.sessionKey}catch(t){if(this.isDestroyed)return;let n=Ut(t);if(n===401&&this.params.refreshAuthToken){if(this.params.errors$.next({id:`ThinOneStat:Api:AuthorizeAuthLogin`,category:G.EXTERNAL_API,message:`Auth token expired during authorization`,data:{error:t,code:n,attempt:e}}),e>=2){this.params.errors$.next({id:`ThinOneStat:Api:AuthorizeMaxAttemptsReached`,category:G.EXTERNAL_API,message:`Authorization failed after ${e} attempts`,data:{error:t,code:n}});return}return this.authToken=await this.refreshAuthToken(),this.isDestroyed?void 0:this.executeAuthorize(e+1)}n?this.params.errors$.next({id:`ThinOneStat:Api:Authorize#${n}`,category:G.EXTERNAL_API,message:`Authorize error`,data:{error:t,code:n}}):this.params.errors$.next({id:`ThinOneStat:Api:AuthorizeUnknown`,category:G.NETWORK,message:`Authorize error`,thrown:t});return}}buildAuthParams(){let e={session_data:{version:2,device_id:this.params.deviceId,client_version:this.getClientVersion(),client_type:`SDK_JS`}};return this.authToken&&(e.session_data.auth_token=this.authToken,e.session_data.version=3),e}getClientVersion(){return Wt(O)}async refreshAuthToken(){if(this.params.refreshAuthToken)return this.refreshAuthTokenPromise||=this.params.refreshAuthToken().catch(e=>{throw this.params.errors$.next({id:`ThinOneStat:Api:RefreshAuthTokenFailed`,category:G.NETWORK,message:`Failed to refresh auth token`,thrown:e}),e}).finally(()=>{this.refreshAuthTokenPromise=void 0}),this.refreshAuthTokenPromise}createLogParams(e){let t=e[0],n=t.application??`@vkontakte/videoplayer-statistics:${this.getClientVersion()}`;return this.params.vktvVersion&&(n=this.params.vktvVersion),{collector:`ok.mobile.apps.video`,uv_stat_data:JSON.stringify({application:n,platform:t.platform,product:t.product,events:e.map(Gt)})}}destroy(){this.isDestroyed=!0,this.authorizePromise=void 0,this.refreshAuthTokenPromise=void 0,this.sessionKey=void 0,this.authorized$.next(!1)}}class qt{id=`uvrest`;authorized$;params;statToken;authorizePromise;isDestroyed=!1;constructor(e){this.params=e,this.authorized$=new zt(!1)}async authorize(e){if(!this.isDestroyed)return this.authorizePromise?this.authorizePromise:(this.authorized$.next(!1),this.authorizePromise=this.executeAuthorize().finally(()=>{this.authorizePromise=void 0,this.isDestroyed||this.authorized$.next(this.statToken!==void 0)}),this.authorizePromise)}async logRequest(e){if(!e.length)return;let t=this.buildBody(e);try{let e=await this.getValidToken();if(!e){this.params.errors$.next({id:`ThinOneStat:UvRestApi:LogRequestNoToken`,category:G.NETWORK,message:`No stat token available for log request`});return}await this.params.apiTransport.sendJsonRequest(`/uv/stat/logUvStat`,t,{Authorization:`Bearer ${e}`})}catch(e){await this.handleLogRequestError(e)&&await this.retryLogRequestAfterRecovery(t)}}logBeacon(e){if(!e.length)return;let t=this.statToken;if(!t)return;let n=this.buildBody(e);this.params.apiTransport.sendJsonBeacon(`/uv/stat/logUvStat`,n,{Authorization:`Bearer ${t}`})}destroy(){this.isDestroyed=!0,this.authorizePromise=void 0,this.statToken=void 0,this.authorized$.next(!1)}async getValidToken(){return this.statToken?this.statToken:this.authorize()}async executeAuthorize(){try{let e=this.params.getAuthToken?await this.params.getAuthToken():await this.selfAuthorize();if(this.isDestroyed)return;if(!e){this.params.errors$.next({id:`ThinOneStat:UvRestApi:NoToken`,category:G.EXTERNAL_API,message:`No stat token obtained`});return}return this.statToken=e,this.statToken}catch(e){if(this.isDestroyed)return;this.params.errors$.next({id:`ThinOneStat:UvRestApi:AuthorizeFailed`,category:G.NETWORK,message:`Failed to get stat token`,thrown:e});return}}async selfAuthorize(){let e=this.params.timeSynchronisation?.now()??Date.now(),t=this.params.userId===void 0?``:String(this.params.userId),n=this.params.config.apiApplicationId,r=this.params.deviceId,i=await xe(`${t}${n}${r}${e}`);if(!i){this.params.errors$.next({id:`ThinOneStat:UvRestApi:SignatureUnavailable`,category:G.WTF,message:`crypto.subtle is unavailable for self authorization`});return}return(await this.params.apiTransport.sendJsonRequest(`/uv/stat/token`,{userId:t,applicationId:n,deviceId:r},{"X-Signature-Timestamp":String(e),Signature:i}))?.token}async handleLogRequestError(e){let t=e?.status;return t===401?(this.params.errors$.next({id:`ThinOneStat:UvRestApi:TokenExpired`,category:G.EXTERNAL_API,message:`Stat token expired (401), refreshing`,thrown:e}),this.authorized$.next(!1),this.statToken=void 0,await this.authorize()!==void 0):(this.params.errors$.next({id:`ThinOneStat:UvRestApi:LogRequestFailed#${t??`unknown`}`,category:G.EXTERNAL_API,message:`Log request failed with status ${t}`,thrown:e}),!1)}async retryLogRequestAfterRecovery(e){let t=this.statToken;if(!t){this.params.errors$.next({id:`ThinOneStat:UvRestApi:RetryNoToken`,category:G.NETWORK,message:`Cannot retry log request: no token after recovery`});return}try{await this.params.apiTransport.sendJsonRequest(`/uv/stat/logUvStat`,e,{Authorization:`Bearer ${t}`})}catch(e){this.params.errors$.next({id:`ThinOneStat:UvRestApi:RetryFailed`,category:G.EXTERNAL_API,message:`Log request failed after token refresh`,thrown:e})}}buildBody(e){let t=e[0],n=t.application;return this.params.vktvVersion&&(n=this.params.vktvVersion),{product:t.product,platform:t.platform,application:n,user_id:this.params.userId,events:e.map(Gt)}}}import{now as K,getWindow as Jt,getExponentialDelay as Yt,isAbortError as Xt,ErrorCategory as q}from"@vkontakte/videoplayer-shared/evergreen";class Zt{params;apiKey;apiBaseUrl;apiUvRestBaseUrl;apiEnv;resolvedApiBaseUrl=null;resolveApiBaseUrlPromise=null;timeSynchronisation;fetchTimeout=3e4;maxTotalRequestTime=6e4;retryConfig;constructor(e){this.params=e,this.apiKey=e.config.apiKey,this.apiEnv=e.config.apiEnv,this.apiBaseUrl=e.config.apiBaseUrl??It[this.apiEnv],this.apiUvRestBaseUrl=e.config.apiUvRestBaseUrl,this.timeSynchronisation=e.timeSynchronisation,this.retryConfig={maxAttempts:e.config.requestRetryCount??3,baseDelay:3e3,maxDelay:3e4,retryableErrors:[`Failed to fetch`,`NetworkError when attempting to fetch resource.`,`Request timeout`,`Network request failed`],retryableStatuses:[408,429,500,502,503,504]}}sendBeacon(e,t,n){if(!Jt().Blob||!Jt().navigator.sendBeacon)return!1;let r=this.resolvedApiBaseUrl??this.apiBaseUrl,i=this.prepareQueryParams({method:e,queryParams:t,sessionKey:n}),a=Jt().Blob,o=new a([i.toString()],{type:`application/x-www-form-urlencoded`});try{return Jt().navigator.sendBeacon(`${r}/fb.do`,o)}catch(n){this.params.errors$.next({id:`ThinOneStat:ApiTransport:sendBeacon`,category:q.NETWORK,message:`Unhandled beacon error`,thrown:n,data:{method:e,params:t}})}return!1}async sendRequest(e,t,n,r){return this.sendRequestWithRetry(e,t,n,r)}async sendRequestWithRetry(e,t,n,r,i=1,a){let o=K(),s=a??o+this.maxTotalRequestTime;if(K()>=s){let r=Error(`Request deadline exceeded`);throw this.handleFinalError(e,t,n,o,r,i),r}try{return await this.executeRequest(e,t,n,r,o,i,s)}catch(a){if(this.shouldRetry(a,i)&&K()<s){let c=this.getRetryDelay(a,i);if(K()+c>=s)throw this.handleFinalError(e,t,n,o,a,i),a;return this.logRetryAttempt(e,i,a,c),await this.sleep(c),this.sendRequestWithRetry(e,t,n,r,i+1,s)}throw this.handleFinalError(e,t,n,o,a,i),a}}async executeRequest(e,t,n,r,i,a,o){let s=i??K(),c=await this.resolveApiBaseUrl(),l=o===void 0?this.fetchTimeout:o-K();if(l<=0)throw Vt();try{let i=await this.fetchWithTimeout(`${c}/fb.do`,{method:`post`,headers:{"Content-type":`application/x-www-form-urlencoded`,...r},body:this.prepareQueryParams({method:e,queryParams:t,sessionKey:n})},Math.min(this.fetchTimeout,l));return await this.processResponse(i,s)}catch(t){throw t instanceof Error&&(t.context={method:e,attempt:a,url:`${c}/fb.do`}),t}}async sendJsonRequest(e,t,n){return this.sendJsonRequestWithRetry(e,t,n)}sendJsonBeacon(e,t,n){let r=`${this.apiUvRestBaseUrl}${e}`;try{fetch(r,{method:`POST`,headers:{"Content-Type":`application/json`,...n},body:JSON.stringify(t),keepalive:!0}).catch(t=>{this.params.errors$.next({id:`ThinOneStat:ApiTransport:sendJsonBeacon`,category:q.NETWORK,message:`Keepalive request failed`,thrown:t,data:{path:e}})})}catch(t){this.params.errors$.next({id:`ThinOneStat:ApiTransport:sendJsonBeacon`,category:q.NETWORK,message:`Failed to initiate keepalive request`,thrown:t,data:{path:e}})}}async sendJsonRequestWithRetry(e,t,n,r=1,i){let a=K(),o=i??a+this.maxTotalRequestTime;if(K()>=o){let t=Error(`Request deadline exceeded`);throw this.handleJsonFinalError(e,a,t,r),t}try{return await this.executeJsonRequest(e,t,n,a,r,o)}catch(i){if(this.shouldRetry(i,r)&&K()<o){let s=this.getRetryDelay(i,r);if(K()+s>=o)throw this.handleJsonFinalError(e,a,i,r),i;return this.logRetryAttempt(e,r,i,s),await this.sleep(s),this.sendJsonRequestWithRetry(e,t,n,r+1,o)}throw this.handleJsonFinalError(e,a,i,r),i}}async executeJsonRequest(e,t,n,r,i,a){let o=r??K(),s=a===void 0?this.fetchTimeout:a-K();if(s<=0)throw Vt();let c=`${this.apiUvRestBaseUrl}${e}`;try{let e=await this.fetchWithTimeout(c,{method:`POST`,headers:{"Content-Type":`application/json`,...n},body:JSON.stringify(t)},Math.min(this.fetchTimeout,s));return await this.processResponse(e,o)}catch(t){throw t instanceof Error&&(t.context={method:e,attempt:i,url:c}),t}}async fetchWithTimeout(e,t,n=this.fetchTimeout){let r=new AbortController,i=A.setTimeout(()=>r.abort(),n);try{let n=await fetch(e,{...t,signal:r.signal});return A.clearTimeout(i),n}catch(e){throw A.clearTimeout(i),Xt(e)?Vt():e}}async processResponse(e,t){if(!e.ok){let t=Bt(e),n=this.parseRetryAfter(e);throw n!==void 0&&(t.retryAfter=n),t}let n=new Date(e.headers.get(`date`)??``).getTime(),r=K()-t;isFinite(n)&&this.timeSynchronisation?.addServerTime(n,r);let i=e.headers.get(`content-length`);if(i!==null&&Number(i)===0)return{};let a=await e.json();if(a&&typeof a==`object`&&`error_msg`in a)throw Ht(a);return a}shouldRetry(e,t){if(t>=this.retryConfig.maxAttempts||!(e instanceof Error))return!1;let n=e;return!!(n.isTimeout||n.status&&this.retryConfig.retryableStatuses.includes(n.status)||this.retryConfig.retryableErrors.includes(e.message)||e.message.includes(`network`)||e.message.includes(`timeout`)||e.message.includes(`connection`))}logRetryAttempt(e,t,n,r){let i=n instanceof Error?n.message:String(n);this.params.errors$.next({id:`ThinOneStat:ApiTransport:retry`,category:q.NETWORK,message:`Retrying request (attempt ${t}/${this.retryConfig.maxAttempts}) after ${Math.round(r)}ms`,thrown:n,data:{method:e,attempt:t,delay:r,errorMessage:i}})}handleFinalError(e,t,n,r,i,a){let o=q.NETWORK;this.params.errors$.next({id:`ThinOneStat:ApiTransport:sendRequest`,category:o,message:i instanceof Error?i.message:`Request failed after retries`,thrown:i,data:{method:e,params:t,sessionKey:n,time:K()-r,maxAttempts:this.retryConfig.maxAttempts,attempts:a}})}handleJsonFinalError(e,t,n,r){this.params.errors$.next({id:`ThinOneStat:ApiTransport:sendJsonRequest`,category:q.NETWORK,message:n instanceof Error?n.message:`JSON request failed after retries`,thrown:n,data:{path:e,time:K()-t,maxAttempts:this.retryConfig.maxAttempts,attempts:r}})}sleep(e){return new Promise(t=>A.setTimeout(t,e))}resolveApiBaseUrl(){return this.apiEnv===`auto`?(this.resolveApiBaseUrlPromise||=this.fetchApiBaseUrl().then(e=>(e===It.vk_alias&&(this.resolveApiBaseUrlPromise=null),e)),this.resolveApiBaseUrlPromise):Promise.resolve(this.apiBaseUrl)}async fetchApiBaseUrl(){try{let e=atob(`aHR0cHM6Ly9kbnMuZ29vZ2xlL3Jlc29sdmU/bmFtZT12aWRlby5fZW5kcG9pbnQub2sucnUmdHlwZT1UWFQ=`),t=(await(await this.fetchWithTimeout(e,{method:`GET`,mode:`cors`,cache:`no-cache`},5e3)).json())?.Answer[0]?.data;if(!t)throw Error(`Wrong DNS response`);return this.resolvedApiBaseUrl=t,t}catch(e){return this.params.errors$.next({id:`ThinOneStat:ApiTransport:resolveApiBaseUrl`,category:q.NETWORK,message:`Unhandled resolve api base url error`,thrown:e}),It.vk_alias}}prepareQueryParams(e){let t=new URLSearchParams({format:`JSON`,method:e.method,application_key:this.apiKey});return e.sessionKey!==void 0&&t.append(`session_key`,e.sessionKey),Object.entries(e.queryParams).forEach(([e,n])=>t.append(e,typeof n==`string`?n:JSON.stringify(n))),t}parseRetryAfter(e){let t=e.headers.get(`Retry-After`);if(!t)return;let n=Number(t);if(!isNaN(n)&&n>=0)return n*1e3;let r=new Date(t).getTime();if(!isNaN(r))return Math.max(0,r-Date.now())}getRetryDelay(e,t){let n=e;return n?.retryAfter!==void 0&&n.retryAfter>0?n.retryAfter:Yt(t,{start:this.retryConfig.baseDelay,max:this.retryConfig.maxDelay,factor:2,random:.2})}}import{ErrorCategory as Qt}from"@vkontakte/videoplayer-shared/evergreen";import{now as $t,debounceFn as en,getWindow as tn,safeStorage as nn,ErrorCategory as J}from"@vkontakte/videoplayer-shared/evergreen";var rn=e=>t=>(t.client_time??$t())+e>=$t();class an{params;subscription;userSalt;unsaltedStorage=[];lastVsid;isPaused=!1;firstFlush;debouncedFlush;flushRetryCount=0;retryFlushTimerId;debugLog;flushPromise=null;flushSnapshotLength=0;onlineListener;urgentEvents=[`watch_coverage`,`watch_coverage_live`,`watched_n`,`playback_started`];STORAGE_MAX_ATTEMPTS=3;constructor(e){this.params=e,this.userSalt=e.userSalt,this.debugLog=e.debugLogger.createComponentLog(`batch-queue:${e.storageKey}`),this.firstFlush=en(()=>this.safeFlush(),this.params.config.flushFirstTime,{maxWait:this.params.config.flushFirstTime});let t=nn.isPersistent()?this.params.config.flushMaxWait:0;this.debouncedFlush=en(()=>this.safeFlush(),this.params.config.flushDebounceTime,{maxWait:t}),this.subscription=this.params.canSend$.subscribe(e=>{e&&(this.debugLog({message:`Can send, triggering flush`}),this.debouncedFlush())}),this.onlineListener=()=>{this.debugLog({message:`Network online, triggering flush`}),this.cancelRetry(),this.safeFlush()},tn().addEventListener(`online`,this.onlineListener),this.housekeepStorage()}isUrgent=e=>this.urgentEvents.includes(e.event_name);scheduleRetry(){this.retryFlushTimerId!==void 0&&A.clearTimeout(this.retryFlushTimerId);let e=this.params.config.flushRetryFirstDelay,t=this.params.config.flushRetryMaxDelay,n=Math.min(e*2**this.flushRetryCount,t);this.flushRetryCount++,this.debugLog({message:`Scheduling flush retry #${this.flushRetryCount} in ${n}ms`}),this.retryFlushTimerId=A.setTimeout(()=>{this.retryFlushTimerId=void 0,this.safeFlush()},n)}cancelRetry(){this.retryFlushTimerId!==void 0&&(A.clearTimeout(this.retryFlushTimerId),this.retryFlushTimerId=void 0),this.flushRetryCount=0}safeFlush(){if(this.flushPromise){this.debugLog({message:`Flush already in progress, skipping`});return}this.flushPromise=this.flush().finally(()=>{this.flushPromise=null})}isStorageAvailable(){let e=nn.isPersistent?.()??!1;return e||this.debugLog({message:`Storage not available`}),e}readFromStorage(){if(!this.isStorageAvailable())return{};try{let e=nn.get(this.params.storageKey),t=e?JSON.parse(e):{};return this.debugLog({message:`Read ${Object.keys(t).length} users from storage`}),t}catch(e){return this.params.errors$.next({id:`ThinOneStat:BatchQueue:ReadStorageError`,category:J.WTF,message:`Failed to read from storage`,thrown:e,data:{storageKey:this.params.storageKey}}),this.debugLog({message:`Failed to read from storage`}),{}}}writeToStorage(e){if(!this.isStorageAvailable())return!1;try{return nn.set(this.params.storageKey,JSON.stringify(e)),this.debugLog({message:`Written ${Object.keys(e).length} users to storage`}),!0}catch(e){return this.params.errors$.next({id:`ThinOneStat:BatchQueue:WriteStorageError`,category:J.WTF,message:`Failed to write to storage`,thrown:e,data:{storageKey:this.params.storageKey}}),this.debugLog({message:`Failed to write to storage`}),!1}}addToStorage(e){if(!this.userSalt){this.unsaltedStorage.push(e),this.debugLog({message:`Added item to unsalted storage, total: ${this.unsaltedStorage.length}`});return}for(let t=0;t<this.STORAGE_MAX_ATTEMPTS;t++)try{let t=this.readFromStorage(),n=t[this.userSalt]??[];if(this.debugLog({message:`Adding item to storage for user ${this.userSalt}`}),this.writeToStorage({...t,[this.userSalt]:[...n,e]}))return}catch(n){t===this.STORAGE_MAX_ATTEMPTS-1&&(this.params.errors$.next({id:`ThinOneStat:BatchQueue:AddToStorageFailed`,category:J.WTF,message:`Failed to add item to storage after retries`,thrown:n,data:{item:e,attempt:t,storageKey:this.params.storageKey}}),this.debugLog({message:`Failed to add item to storage after retries`}))}}getFromStorage(){if(!this.userSalt)return this.debugLog({message:`Retrieved ${this.unsaltedStorage.length} items from unsalted storage`}),this.flushSnapshotLength=this.unsaltedStorage.length,this.unsaltedStorage.slice();let e=this.readFromStorage(),t=e[this.userSalt]??[],n=t.filter(rn(this.params.config.storageExpiration));if(this.debugLog({message:`Retrieved from storage for user ${this.userSalt}`}),n.length!==t.length){let r=t.length-n.length;this.params.errors$.next({id:`ThinOneStat:BatchQueue:DropExpiredEvents`,category:J.WTF,message:`Dropped ${r} expired events from storage`,data:{droppedCount:r,expiration:this.params.config.storageExpiration,storageKey:this.params.storageKey}}),this.debugLog({message:`Cleaning ${r} expired items from storage`});let i={...e};n.length?i[this.userSalt]=n:delete i[this.userSalt],this.writeToStorage(i)}return this.flushSnapshotLength=n.length,n}markStorageSent(){if(!this.userSalt){let e=this.unsaltedStorage.splice(0,this.flushSnapshotLength);this.debugLog({message:`Cleared unsalted storage, removed ${e.length} items, ${this.unsaltedStorage.length} new items kept`}),this.flushSnapshotLength=0;return}let e=this.flushSnapshotLength;this.flushSnapshotLength=0;for(let t=0;t<this.STORAGE_MAX_ATTEMPTS;t++)try{let t=this.readFromStorage(),n=(t[this.userSalt]??[]).slice(e),r={...t};if(n.length?r[this.userSalt]=n:delete r[this.userSalt],this.writeToStorage(r)){this.debugLog({message:`Marked storage as sent for user ${this.userSalt}, removed ${e} items, ${n.length} kept`});return}}catch(e){t===this.STORAGE_MAX_ATTEMPTS-1&&(this.params.errors$.next({id:`ThinOneStat:BatchQueue:MarkStorageSentFailed`,category:J.WTF,message:`Failed to mark storage as sent after retries`,thrown:e,data:{storageKey:this.params.storageKey}}),this.debugLog({message:`Failed to mark storage as sent after retries`}))}}housekeepStorage(){if(this.isStorageAvailable())try{let e=this.readFromStorage(),t=!1,n=0;for(let[r,i]of Object.entries(e)){let a=i.filter(rn(this.params.config.storageExpiration)),o=i.length-a.length;n+=o,a.length?a.length!==i.length&&(e[r]=a,t=!0):(delete e[r],t=!0),this.debugLog({message:`${i.length} retrieved from storage, ${a.length} of them actual for ${r}`})}t?(this.debugLog({message:`Housekeeping completed, removed ${n} expired items total`}),this.writeToStorage(e),n>0&&this.params.errors$.next({id:`ThinOneStat:BatchQueue:DropExpiredEvents`,category:J.WTF,message:`Dropped ${n} expired events during housekeeping`,data:{droppedCount:n,expiration:this.params.config.storageExpiration,storageKey:this.params.storageKey}})):this.debugLog({message:`Housekeeping completed, no changes needed`})}catch(e){this.params.errors$.next({id:`ThinOneStat:BatchQueue:HousekeepStorageError`,category:J.WTF,message:`Failed to housekeep storage`,thrown:e,data:{storageKey:this.params.storageKey}}),this.debugLog({message:`Failed to housekeep storage`})}}log(e){try{if(this.debugLog({message:`Logging item: ${e.event_name}`}),this.addToStorage(e),this.isPaused){this.debugLog({message:`Queue paused, skipping flush`});return}this.isUrgent(e)?(this.debugLog({message:`Urgent item, flushing immediately`}),this.safeFlush()):this.lastVsid===e.vsid?(this.debugLog({message:`Same VSID, scheduling debounced flush`}),this.debouncedFlush()):(this.debugLog({message:`VSID changed, triggering first flush`}),this.firstFlush()),this.lastVsid=e.vsid}catch(t){this.params.errors$.next({id:`ThinOneStat:BatchQueue:LogError`,category:J.WTF,message:`Failed to log item`,thrown:t,data:{item:e,storageKey:this.params.storageKey}}),this.debugLog({message:`Failed to log item`})}}flush(e={wantBeacon:!1,clearStorage:!0}){return this.flushPromise?(this.debugLog({message:`Flush already in progress, waiting`}),this.flushPromise):(this.flushPromise=this.flushInternal(e).finally(()=>{this.flushPromise=null}),this.flushPromise)}async flushInternal({wantBeacon:e,clearStorage:t}){this.debugLog({message:`Starting flush`});let n=this.getFromStorage();if(n.length===0){this.debugLog({message:`No items to flush`});return}let r=this.params.canSend$.getValue();if(this.debugLog({message:`Can send status: ${r}`}),!r){this.debugLog({message:`Cannot send, requesting ready state`}),this.params.requestReady();return}if(e&&this.params.config.useBeacon){this.debugLog({message:`Flushing ${n.length} events through beacon`}),this.params.sendBeacon(n),t&&this.markStorageSent(),this.firstFlush.cancel(),this.debouncedFlush.cancel();return}this.debugLog({message:`Flushing ${n.length} events`});try{await this.params.send(n),this.debugLog({message:`Flush completed successfully`}),this.cancelRetry(),t&&this.markStorageSent(),this.firstFlush.cancel(),this.debouncedFlush.cancel()}catch(e){throw this.params.errors$.next({id:`ThinOneStat:BatchQueue:FlushRequestError`,category:J.NETWORK,message:`Failed to send events, will retry later`,thrown:e,data:{itemsCount:n.length,storageKey:this.params.storageKey}}),this.debugLog({message:`Flush failed, scheduling retry`}),this.scheduleRetry(),e}}pause(){this.debugLog({message:`Queue paused`}),this.isPaused=!0,this.debouncedFlush.cancel(),this.firstFlush.cancel(),this.cancelRetry()}resume(){this.debugLog({message:`Queue resumed`}),this.isPaused=!1,this.debouncedFlush()}async destroy(){this.debugLog({message:`Destroying queue`}),this.flushPromise&&(this.debugLog({message:`Waiting for ongoing flush to complete`}),await this.flushPromise),this.cancelRetry(),this.onlineListener&&=(tn().removeEventListener(`online`,this.onlineListener),void 0),this.subscription.unsubscribe(),this.firstFlush.cancel(),this.debouncedFlush.cancel(),this.flushPromise=null,this.debugLog({message:`Queue destroyed`})}}class on{params;queues=[];constructor(e){this.params=e;for(let t of e.apis)this.queues.push(this.createQueueForApi(t))}createQueueForApi(e){let t=this.getStorageKeyForApi(e.id);return new an({config:this.params.config,storageKey:t,debugLogger:this.params.debugLogger,errors$:this.params.errors$,userSalt:this.params.userSalt,send:t=>e.logRequest(t),sendBeacon:t=>e.logBeacon(t),canSend$:e.authorized$,requestReady:()=>{e.authorize().catch(e=>{this.params.errors$.next({id:`ThinOneStat:Logger:RequestReadyFailed`,category:Qt.NETWORK,message:`Failed to authorize on requestReady`,thrown:e,data:{storageKey:t}})})}})}getStorageKeyForApi(e){return`thinonestat_events_${e}`}log(e){this.queues.forEach(t=>t.log(e))}async flush(e){let t=await Promise.allSettled(this.queues.map(t=>t.flush(e)));if(t.length>0&&t.every(e=>e.status===`rejected`))throw t.find(e=>e.status===`rejected`).reason}pause(){this.queues.forEach(e=>e.pause())}resume(){this.queues.forEach(e=>e.resume())}async destroy(){await Promise.allSettled(this.queues.map(e=>e.destroy())),this.queues=[]}}var sn=new Set(Ot),cn=e=>sn.has(e);function ln(){let e=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`.split(``),t=Array(36),n=0,r,i;for(i=0;i<36;i++)i===8||i===13||i===18||i===23?t[i]=`-`:i===14?t[i]=`4`:(n<=2&&(n=33554432+Math.random()*16777216|0),r=n&15,n>>=4,t[i]=e[i===19?r&3|8:r]);return t.join(``)}class un{offset=void 0;constructor(e){this.offset=e}getOffset(){return this.offset??0}setOffset(e){this.offset=e}now(){return Date.now()+(this.offset??0)}date(e=new Date){return e.setTime(e.getTime()+(this.offset??0)),e}addServerTime(e,t){let n=e-Date.now()-t/2;if(Math.abs(n)<1e3){this.offset=0;return}if(this.offset===void 0)this.offset=Math.round(n);else{let e=.2;this.offset=Math.round(e*n+(1-e)*this.offset)}}}import{isNullable as dn}from"@vkontakte/videoplayer-shared/evergreen";var fn=Date.UTC(2020,0,1),pn=Date.UTC(2100,0,1),mn=720*60*60*1e3,hn=2**31-1,gn=e=>!dn(e)&&isFinite(e)&&e>=fn&&e<=pn,_n=e=>gn(e)?e:Date.now(),vn=e=>(e??0)*1e3,yn=e=>isFinite(e)&&e>=0&&e<=mn,bn=e=>isFinite(e)?Math.min(Math.max(e,0),hn):0;import{clientChecker as xn}from"@vkontakte/videoplayer-core/evergreen";import{isNonNullable as Sn}from"@vkontakte/videoplayer-shared/evergreen";var Cn=()=>{let{device:e,display:t}=xn,{cpuCores:n,memoryGb:r,gpuVendor:i,gpuArchitecture:a}=e.hardware,{architecture:o,bitness:s}=e.highEntropyValues,c={cpu_cores:n,memory_gb:r,architecture:o,bitness:s,gpu_vendor:i,gpu_architecture:a,screen_width:t.width,screen_height:t.height,pixel_ratio:t.pixelRatio,color_depth:t.colorDepth,color_gamut:t.colorGamut,hdr_supported:t.isHDR,max_touch_points:t.maxTouchPoints};return Object.fromEntries(Object.entries(c).filter(([,e])=>Sn(e)))};import{PlaybackState as wn}from"@vkontakte/videoplayer-core/evergreen";import{Observable as Tn,Subscription as En,Subject as Dn,fromEvent as On,isNullable as kn,map as An,merge as Y,filter as jn,filterChanged as Mn,getWindow as Nn}from"@vkontakte/videoplayer-shared/evergreen";var Pn=1440*60*1e3,Fn=(e,t,n)=>new Tn(r=>{let i=new En,a,o=!1;i.add(e.info.isLive$.pipe(Mn()).subscribe(e=>{a=void 0,o=!!e}));let{playing$:s,paused$:c,ended$:l,looped$:u}=e.events,d=e.events.willSeek$.pipe(jn(()=>e.info.playbackState$.getValue()===wn.PLAYING)),f=e.events.seeked$.pipe(jn(()=>e.info.playbackState$.getValue()===wn.PLAYING)),p=e.info.isStalled$.pipe(jn(e=>e)),m=!1,h=new Dn;i.add(d.subscribe(()=>{m||h.next(),m=!0})).add(f.subscribe(()=>m=!1));let g=Y(On(Nn(),`beforeunload`),e.events.willDestruct$),ee=e=>{a=e},_=e=>{let t=a;if(a=void 0,!(kn(t)||kn(e)||!isFinite(t)||!isFinite(e))){if(Math.round(e)<=Math.round(t)){a=e;return}if(e-t>Pn){n.onInvalidInterval?.({from:t,to:e});return}r.next({from:t,to:e})}},v=new Dn,y=new Dn,b=()=>o?_n(n.exactTime?vn(e.getExactLiveTime()):e.info.liveTime$.getValue()):vn(n.exactTime?e.getExactTime():e.info.position$.getValue()),x=Y(s,f,v).pipe(An(()=>b())),S=Y(u,Y(c,h,g,l,y,p).pipe(An(()=>b()))),te=t.started$&&t.ended$?Y(x,t.ended$):x,C=t.started$&&t.ended$?Y(S,t.started$):S;if(i.add(te.subscribe(ee)).add(C.subscribe(_)),n.heartbeatInterval&&isFinite(n.heartbeatInterval)){let e=[],t=()=>{y.next(),v.next()};i.add(te.subscribe(()=>{let r=A.setTimeout(t,n.heartbeatInterval);e.push(r)})).add(C.subscribe(()=>{e.forEach(e=>{A.clearTimeout(e)}),e=[]}))}return i});import{Subscription as In}from"@vkontakte/videoplayer-core/evergreen";import{Observable as Ln,merge as Rn}from"@vkontakte/videoplayer-shared/evergreen";var zn=(e,t)=>new Ln(n=>{let r=new In;return r.add(Rn(e.info.position$,e.info.liveTime$).subscribe(()=>{t.getTotalViewTime()>=t.targetDuration&&(n.next({target_duration:t.targetDuration}),r.unsubscribe())})),r});import{Subscription as Bn}from"@vkontakte/videoplayer-core/evergreen";import{Observable as Vn,filter as Hn,now as Un}from"@vkontakte/videoplayer-shared/evergreen";var Wn=(e,{maxTimeWindow:t})=>new Vn(n=>{let r=new Bn,i={maxBytesThreshold:100*1024*1024,minBytesThreshold:100*1024,maxTimeWindow:t,minTimeWindow:2e3},a={bytes:0,startTime:0,lastEmitTime:0,timeoutId:0},o=()=>{let t=e.info.throughputEstimation$.getValue();return t?Math.round(t):void 0},s=()=>{a.timeoutId&&=(A.clearTimeout(a.timeoutId),null)},c=()=>{a.bytes=0,a.startTime=0,a.lastEmitTime=0,s()},l=e=>{if(a.bytes<i.minBytesThreshold||e-a.lastEmitTime<i.minTimeWindow&&a.lastEmitTime>0)return!1;let t=(a.startTime>0?e-a.startTime:0)>=i.maxTimeWindow,n=a.bytes>=i.maxBytesThreshold;return t||n},u=(e=!1)=>{let t=Un();!e&&!l(t)||(s(),n.next({download_bytes:Math.round(a.bytes),download_speed:o()}),a.lastEmitTime=t,c())},d=()=>{if(s(),a.startTime>0&&a.bytes>=i.minBytesThreshold){let e=Un()-a.startTime,t=i.maxTimeWindow-e;t>100?a.timeoutId=A.setTimeout(()=>{a.timeoutId=null,u()},t):t>0&&u()}};return r.add(e.info.httpDownloadMetrics$.pipe(Hn(e=>e!==void 0)).subscribe(e=>{let t=Un();a.startTime===0&&(a.startTime=t),a.bytes+=e.bytes,l(t)?u():d()})),r.add(()=>{s(),a.bytes>0&&n.next({download_bytes:Math.round(a.bytes),download_speed:o()}),c()}),r}),Gn=({buffered:e,currentTime:t})=>{for(let n=0;n<e.length;n++)if(e.start(n)<=t&&t<=e.end(n))return Math.round((e.end(n)-t)*1e3)};import{Observable as Kn,Subscription as qn,merge as Jn,now as Yn}from"@vkontakte/videoplayer-shared/evergreen";var Xn=e=>new Kn(t=>{let n=new qn,r=0,i=1,a=0,o=!1,s=!1,c=()=>{if(!o)return;let e=Yn();a+=(e-r)*i,r=e},l=()=>{(!o||s)&&(r=Yn(),o=!0,s=!1)},u=()=>{o&&=(c(),!1)},d=()=>{o&&(c(),o=!1,s=!0)},f=e=>{o&&c(),i=e,r=Yn()},{started$:p,playing$:m,paused$:h,willSeek$:g,ended$:ee,willDestruct$:_}=e.events,{currentPlaybackRate$:v}=e.info;return n.add(g.subscribe(d)).add(Jn(p,m).subscribe(l)).add(Jn(h,ee,_).subscribe(u)).add(v.subscribe(f)),t.next(()=>{let e=a;return o&&(e+=(Yn()-r)*i),Math.round(e)}),n});import{Observable as Zn,Subscription as Qn,merge as $n}from"@vkontakte/videoplayer-shared/evergreen";var er=(e,t)=>new Zn(n=>{let r=new Qn,i=0;return r.add($n(e.info.position$,e.info.liveTime$).subscribe(()=>{let e=t.getTotalViewTime();e>i&&Math.floor(e/t.interval)>Math.floor(i/t.interval)&&(i=e,n.next())})),r});import{assertNever as tr,isNullable as nr}from"@vkontakte/videoplayer-shared/evergreen";import{HttpConnectionType as rr,Surface as X,VideoFormat as Z,VideoQuality as Q}from"@vkontakte/videoplayer-core/evergreen";var ir=e=>{if(!nr(e)){if(typeof e==`string`)return e;try{return JSON.stringify(e)}catch{return String(e)}}},ar=e=>e&&{[rr.HTTP1]:`http1`,[rr.HTTP2]:`http2`,[rr.QUIC]:`http3`}[e],or=e=>{if(e!==void 0)switch(e){case Z.MPEG:return`MP4`;case Z.DASH:case Z.DASH_LIVE:return`DASH`;case Z.DASH_SEP:return`DASH_SEP`;case Z.DASH_ONDEMAND:return`ONDEMAND_DASH`;case Z.HLS_ONDEMAND:return`ONDEMAND_HLS`;case Z.HLS:case Z.HLS_LIVE:return`HLS`;case Z.HLS_FMP4:return`HLS_FMP4`;case Z.DASH_WEBM:case Z.DASH_LIVE_WEBM:return`WEBM`;case Z.DASH_LIVE_CMAF:return`ONDEMAND_DASH_LIVE`;case Z.HLS_LIVE_CMAF:return`ONDEMAND_HLS_LIVE`;case Z.WEB_RTC_LIVE:return`WEBRTC`;case Z.DASH_WEBM_AV1:return`AV1`;case Z.DASH_STREAMS:return`MULTI_DASH`;default:return tr(e)}},$=e=>{if(e!==void 0)switch(e){case Q.Q_144P:return`144p`;case Q.Q_240P:return`240p`;case Q.Q_360P:return`360p`;case Q.Q_480P:return`480p`;case Q.Q_720P:return`720p`;case Q.Q_1080P:return`1080p`;case Q.Q_1440P:return`1440p`;case Q.Q_2160P:return`2160p`;case Q.INVARIANT:case Q.Q_576P:case Q.Q_4320P:return`UNKNOWN`;default:return tr(e)}},sr=e=>{if(e!==void 0)switch(e){case`MP4`:case`ONDEMAND_DASH`:case`ONDEMAND_DASH_LIVE`:case`ONDEMAND_HLS`:case`ONDEMAND_HLS_LIVE`:case`MULTI_DASH`:case`WEBM`:case`AV1`:return`vod`;case`DASH`:case`HLS`:case`HLS_FMP4`:case`WEBRTC`:return`live`;default:return}},cr=(e,t,n)=>{if(e&&t)switch(e){case Z.MPEG:return n&&t[e]&&t[e][n];default:return t[e]?.url}},lr=(e,t=!1)=>{if(t)return`minimal`;switch(e){case void 0:case X.NONE:return;case X.INLINE:return`inline`;case X.FULLSCREEN:return`fullscreen`;case X.SECOND_SCREEN:return`chromecast`;case X.PIP:return`pip_external`}},ur=e=>{switch(e){case void 0:return;case X.INVISIBLE:return`background`;default:return`foreground`}},dr=`_thin-one-stat_deviceId`,fr=`2.2.0`,pr=-1,mr=()=>{let e=new H;return{subscription:e,subscribe:(t,n)=>{t&&e.add(t.subscribe(n))}}};class hr{apis=[];logger;config;subscription;lifecycleFlushSubscription;timeSynchronisation;statContext;deviceId;targetDuration;debugLogger=new gt;thinOneStatDebugLog=this.debugLogger.createComponentLog(`ThinOneStat`);onceEvents=new Set(At);sentOnceEvents=new Set;eventNumber=1;sessionMode=`common`;isStarted;isSeeking;inBufferStarvation;isLive;lastContentType;cdnHostname;isEmbed;embedHostname;connectionType;connectionReused;player;loopCount=0;loopLimitReached=!1;playerSize={width:0,height:0};currentSubtitle;userQuality;downloadQuality;networkType;uiEvents;playerSubscription;uiSubscription;adsSubscription;seekAction$=new Et(`unknown`);vsid$=new Et(void 0);isid$=new Et(void 0);errors$=new Tt;getTotalViewTime=()=>0;constructor(e,t){this.statContext=e,this.config=Rt(t.config??{}),this.subscription=new H,this.config.synchronizeTime&&(this.timeSynchronisation=new un),this.subscription.add(this.errors$.subscribe(e=>{this.thinOneStatDebugLog({message:`[${e.category}] ${e.id}: ${e.message}`})}));let n=Ct.get(dr);this.statContext.deviceId?this.deviceId=this.statContext.deviceId:n?this.deviceId=n:(this.deviceId=ln(),Ct.set(dr,this.deviceId)),this.statContext.targetDuration?this.targetDuration=this.statContext.targetDuration:this.targetDuration=this.config.watchedNDefaultTargetDuration,this.resetViewSession(),t.useIsid&&this.isid$.next(t.isid??ve());let r=new Zt({errors$:this.errors$,config:this.config,timeSynchronisation:this.timeSynchronisation});this.config.apiForticom&&this.apis.push(new Kt({config:this.config,apiTransport:r,errors$:this.errors$,deviceId:this.deviceId,refreshAuthToken:t.refreshAuthToken})),this.config.apiUvRest&&this.apis.push(new qt({config:this.config,apiTransport:r,errors$:this.errors$,userId:this.statContext.userId,deviceId:this.deviceId,getAuthToken:t.getUvRestAuthToken,timeSynchronisation:this.timeSynchronisation})),this.logger=new on({config:this.config,debugLogger:this.debugLogger,apis:this.apis,errors$:this.errors$,userSalt:t.userSalt});let{isEmbed:i,topOrigin:a}=mt();this.isEmbed=i,this.embedHostname=a?new URL(a).hostname:void 0}authorize(e){this.apis.forEach(t=>{t.authorize(e).catch(()=>{})})}reportError(e){let t={error_severity:e.severity,error_category:this.mapErrorCategory(e.category),player_error_code:e.code,player_error_trace:e.trace,error_message:e.message,content_type:this.getCurrentContentType()};this.logError(t),e.severity===`critical`&&this.logCannotPlay(),this.logTelemetry(this.collectTelemetryData())}updateContext(e){this.statContext={...this.statContext,...e}}attachTo(e,t){this.playerSubscription&&(this.thinOneStatDebugLog({message:`Re-attaching to player, unsubscribing from previous`}),this.playerSubscription.unsubscribe());let n=new H,r=(e,t)=>n.add(e.subscribe(t));r(Xn(e),e=>{this.getTotalViewTime=e}),r(e.info.isLive$,e=>this.isLive=e),r(e.info.currentFormat$,e=>{let t=or(e);t&&(this.lastContentType=t)});let i;r(e.info.hostname$.pipe(V(e=>e!==void 0),vt()),e=>{if(i!==void 0&&i!==e){let t={cdn_host:e,connection_type:this.connectionType,connection_reused:this.connectionReused,content_type:this.getCurrentContentType()};this.logFailover(t),this.logTelemetry(this.collectTelemetryData())}this.cdnHostname=i=e}),r(e.info.httpConnectionType$,e=>{this.connectionType=ar(e)}),r(e.info.httpConnectionReused$,e=>{this.connectionReused=e}),r(Fn(e,t,{heartbeatInterval:this.config.watchCoverageHeartbeatInterval,exactTime:this.config.watchCoverageExactTime,onInvalidInterval:({from:e,to:t})=>this.thinOneStatDebugLog({message:`invalid watch interval: ${Math.round(e)}-${Math.round(t)}`})}),t=>{let n={watch_interval:`${Math.round(t.from)}-${Math.round(t.to)}`,in_history:!!this.statContext.inHistory,content_type:this.getCurrentContentType(),playback_quality:$(e.info.currentQuality$.getValue()),recom_info:this.statContext.recomInfo};if(!e.info.isLive$.getValue())this.logWatchCoverage(n);else{let t=e.info.atLiveEdge$.getValue();this.logWatchCoverageLive({...n,live:!!t})}}),r(er(e,{interval:this.statContext.telemetryInterval??this.config.telemetryInterval,getTotalViewTime:this.getTotalViewTime}),()=>{this.logTelemetry(this.collectTelemetryData())}),r(zn(e,{targetDuration:this.targetDuration,getTotalViewTime:this.getTotalViewTime}),e=>{this.logWatchedN(e)}),r(_t(e.events.willReady$,e.events.willStart$),()=>{let e={isid:this.isid$.getValue(),stream_profile:sr(this.getCurrentContentType()),dpi:Math.round(this.statContext.dpi??96*W.display.pixelRatio),web_layout:this.statContext.isMobile||W.device.isMobile?`mobile`:`desktop`,preloaded:this.statContext.preload,navigation:this.statContext.navigation,recom_info:this.statContext.recomInfo};this.isEmbed&&(e.iframe_host=this.embedHostname??`unknown`),this.logStartSession(e)}),r(e.events.willReady$,()=>{if(this.statContext.preload){let t=e.info.currentFormat$.getValue(),n=e.info.availableSources$.getValue(),r=e.info.currentVideoBuffer$.getValue(),i=e.info.currentQuality$.getValue(),a={target_buffer_time:this.calcBufferTime(r),playback_url:cr(t,n,i),playback_quality:$(i)};this.logPreloadStarted(a)}});let a=()=>({playback_url:cr(e.info.currentFormat$.getValue(),e.info.availableSources$.getValue(),e.info.currentQuality$.getValue()),cdn_host:this.cdnHostname,connection_type:this.connectionType,connection_reused:this.connectionReused});r(B({manifestRequested$:e.events.manifestRequested$,connectionType:e.info.httpConnectionType$,connectionReused:e.info.httpConnectionReused$}).pipe(bt()),()=>{this.logManifestRequest(a())}),r(B({firstBytesManifest:e.events.firstBytesManifest$,connectionType:e.info.httpConnectionType$,connectionReused:e.info.httpConnectionReused$}).pipe(bt()),()=>{this.logFirstByteManifest(a())}),r(e.events.manifestReceived$,()=>{this.logManifestReceived(a())}),r(e.events.firstBytesRequested$,()=>{this.logFirstMediaRequest(a())}),r(e.events.firstBytes$,()=>{this.logFirstByteMedia(a())}),r(e.events.willStart$,()=>{this.isStarted=!0}),r(e.events.started$,()=>{let t=e.info.currentQuality$.getValue(),r=e.info.isAutoQualityEnabled$.getValue(),i=e.info.muted$.getValue(),a=e.info.volume$.getValue(),o=e.info.surface$.getValue(),s=e.info.availableTextTracks$.getValue(),c=e.info.currentTextTrack$.getValue(),l=e.info.currentAudioStream$.getValue(),u=e.info.currentPlaybackRate$.getValue(),d={playback_quality:$(t),user_quality:r?`auto`:$(t),player_width:Math.round(this.statContext.playerSize?.width??this.playerSize.width),player_height:Math.round(this.statContext.playerSize?.height??this.playerSize.height),muted:i,sound_volume:Math.round(a*100),buffer_time:this.getBufferTime(e),mode:lr(o,this.statContext.isPreviewPlayerView),visibility:ur(o),subtitles_enabled:c?!!c:this.currentSubtitle?.enabled,auto_subtitles:s.find(({id:e})=>e===c)?.isAuto??this.currentSubtitle?.auto,audio_track_lang:l?.language,playback_rate:u*100,recom_info:this.statContext.recomInfo};this.logPlaybackStarted(d),this.logTelemetry(this.collectTelemetryData());let f=wt().requestIdleCallback(()=>void W.device.detectGpu());n.add(()=>wt().cancelIdleCallback(f))}),r(e.events.willStart$,()=>{this.logPlay()}),r(e.events.willResume$,()=>{this.logResume()}),r(e.events.playing$,()=>{let t={buffer_time:this.getBufferTime(e)};this.logPlaying(t),this.inBufferStarvation=!1}),r(e.events.paused$,()=>{this.logPause()}),r(_t(e.events.ended$,e.events.looped$),()=>{this.logContentEndReached(),++this.loopCount>this.config.maxLoops&&(this.loopLimitReached=!0)}),r(e.events.willStop$,()=>{this.isStarted=!1,this.isSeeking=!1,this.inBufferStarvation=!1,this.logStop()}),r(_t(e.events.fetcherRecoverableError$,e.events.fatalError$),()=>{this.statContext.preload&&this.logPreloadError()}),r(e.events.fetcherRecoverableError$,({id:e,category:t,data:n,message:r,httpCode:i,UVBackendErrorCode:a})=>{let o={error_severity:this.mapErrorSeverity(t),error_category:this.mapErrorCategory(t),player_error_code:e,player_error_trace:ir(n),http_error_code:i,uv_backend_error_code_subcode:a,error_message:r,content_type:this.getCurrentContentType()};this.logError(o),this.logTelemetry(this.collectTelemetryData())}),r(e.events.managedError$,({id:e,category:t,data:n,message:r,httpCode:i,UVBackendErrorCode:a})=>{let o={error_severity:this.mapErrorSeverity(t),error_category:this.mapErrorCategory(t),player_error_code:e,player_error_trace:ir(n),http_error_code:i,uv_backend_error_code_subcode:a,error_message:r,content_type:this.getCurrentContentType()};this.logError(o),this.logTelemetry(this.collectTelemetryData())}),r(e.events.fatalError$,({id:e,category:t,data:n,message:r,httpCode:i,UVBackendErrorCode:a})=>{let o={error_severity:`critical`,error_category:this.mapErrorCategory(t),player_error_code:e,player_error_trace:ir(n),http_error_code:i,uv_backend_error_code_subcode:a,error_message:r,content_type:this.getCurrentContentType()};this.logError(o),this.logCannotPlay(),this.logTelemetry(this.collectTelemetryData())}),r(B({connectionType:e.info.httpConnectionType$,connectionReused:e.info.httpConnectionReused$,connectionMetrics:e.info.httpConnectionMetrics$}).pipe(V(({connectionReused:e})=>e!==void 0),V(({connectionMetrics:e})=>e!==void 0),vt((e,t)=>e.connectionType===t.connectionType)),({connectionMetrics:t})=>{let n=e.info.currentFormat$.getValue(),r=e.info.availableSources$.getValue(),i=e.info.currentQuality$.getValue(),a=e.info.rttEstimation$.getValue();this.networkType=this.statContext.networkType??t?.networkType;let o=t?.dnsResolveTime,s=t?.tcpHandshakeTime,c=t?.tlsHandshakeTime,l={playback_url:cr(n,r,i),cdn_host:this.cdnHostname,connection_type:this.connectionType,connection_reused:this.connectionReused,rtt:a?Math.round(a):void 0,network_type:this.networkType,dns_resolve_time:o?Math.round(o):void 0,tcp_handshake_time:s?Math.round(s):void 0,tls_handshake_time:c?Math.round(c):void 0};this.logConnectionEstablished(l)}),r(Wn(e,{maxTimeWindow:this.config.downloadBytesMaxTime}),e=>{this.logDownloadBytes(e)}),r(e.events.firstFrame$,()=>{this.logFirstVideoFrameDecoded()}),r(e.events.willSeek$,()=>{if(this.isSeeking||!this.isStarted)return;this.isSeeking=!0;let e={seek_type:this.seekAction$.getValue()};this.logSeeking(e)}),r(e.events.seeked$,()=>{if(!this.isSeeking)return;this.isSeeking=!1;let e={seek_type:this.seekAction$.getValue()};this.logSeeked(e),this.seekAction$.next(`unknown`)}),r(e.info.stallStartTime$,t=>{if(t<=0){this.inBufferStarvation=!1;return}!this.isStarted||this.inBufferStarvation||(this.inBufferStarvation=!0,this.logBufferStarvation({buffer_time:this.getBufferTime(e)}),this.logTelemetry(this.collectTelemetryData()))}),r(e.events.canplay$,()=>{let t=this.getBufferTime(e);if(this.statContext.preload){let e={buffer_time:t};this.logPreloadEnded(e)}this.logReady({buffer_time:t})});let o;r(B({videoStream:e.info.currentVideoStream$,audioStream:e.info.currentAudioStream$,quality:e.info.currentQuality$}).pipe(V(({videoStream:e,audioStream:t})=>e!==void 0&&t!==void 0),V(({quality:e})=>e!==void 0)),({videoStream:t,audioStream:n,quality:r})=>{if(o!==r){let i=e.info.isAutoQualityEnabled$.getValue();if(this.userQuality=i?`auto`:$(r),this.downloadQuality=$(r),this.isStarted){let e=t?.codec,i=n?.codec,a={user_quality:this.userQuality,playback_quality:$(r),download_quality:this.downloadQuality,codec_info:this.genCodecInfo(e,i)};this.logQualityChanged(a)}o=r}});let s,c;return r(e.info.surface$,e=>{let t=lr(e,this.statContext.isPreviewPlayerView);t&&!this.uiEvents&&this.isStarted&&this.logModeChanged({mode:t}),c=ur(e),s!==c&&(this.isStarted&&this.logVisibilityChanged({visibility:c}),s=ur(e))}),r(B({muted:e.info.muted$,volume:e.info.volume$}).pipe(St(300)),({muted:e,volume:t})=>{if(this.isStarted){let n={muted:e,sound_volume:Math.round(t*100)};this.logSoundChanged(n)}}),r(e.info.currentFormat$.pipe(V(e=>e!==void 0),xt(1)),e=>{let t={content_type:or(e),cdn_host:this.cdnHostname,connection_type:this.connectionType,connection_reused:this.connectionReused};this.logContentTypeChange(t)}),r(e.info.currentAudioStream$.pipe(V(e=>e!==void 0),xt(1)),e=>{let t={audio_track_lang:e.language,codec_info:e.codec};this.logAudioTrackSwitched(t)}),r(e.info.currentVideoStream$.pipe(V(e=>e!==void 0),xt(1)),e=>{let t={hdr:e.hdr,codec_info:e.codec};this.logVideoTrackSwitched(t)}),this.player=e,this.playerSubscription=n,this.resubscribeLifecycleFlush(),this.subscription.add(n),n}attachToUi(e){this.uiSubscription&&(this.thinOneStatDebugLog({message:`Re-attaching to UI, unsubscribing from previous`}),this.uiSubscription.unsubscribe()),this.uiEvents=e;let{subscribe:t,subscription:n}=mr();return t(e.actionSeek$,this.seekAction$),t(e.playerSize$,({width:e,height:t})=>{e&&t&&(this.playerSize={width:e,height:t},this.isStarted&&this.logViewPortChanged({player_width:Math.round(e),player_height:Math.round(t)}))}),t(e.isLoaderVisible$,e=>{!e||!this.isStarted||this.logShowLoader()}),t(e.actionSubtitlesSwitched$,e=>{if(this.currentSubtitle={...e},this.isStarted){let t={subtitles_enabled:e.enabled,subtitles_track_lang:e.lang,auto_subtitles:e.auto};this.logSubtitlesSwitched(t)}}),ot({uiEvents:this.uiEvents,initExternalSubscribers:e=>{t(e,e=>{this.isStarted&&this.logModeChanged({mode:lr(e)})})},subscribe:t,player:this.player}),t(e.actionQuality$,e=>{let t=e=>Object.values(jt).includes(e),n=this.player?.info.currentVideoStream$.getValue()?.codec,r=this.player?.info.currentAudioStream$.getValue()?.codec,i={user_quality:t(e)?`auto`:$(e),playback_quality:$(this.player?.info.currentQuality$.getValue()),download_quality:t(e)?$(this.player?.info.currentQuality$?.getValue()):$(e),codec_info:this.genCodecInfo(n,r)};this.logQualityChangeRequested(i)}),t(e.fatalError$,e=>{if(!e)return;let{type:t,message:n}=e,r={severity:`critical`,category:U.FATAL,code:t,message:n};this.reportError(r)}),this.uiSubscription=n,this.resubscribeLifecycleFlush(),this.subscription.add(n),n}attachToAds(e){this.adsSubscription&&(this.thinOneStatDebugLog({message:`Re-attaching to ads, unsubscribing from previous`}),this.adsSubscription.unsubscribe());let{subscription:t,subscribe:n}=mr();return n(e.init$,e=>{let t={slot:e};this.logAdvConfiguration(t)}),n(e.slotRequested$,e=>{let t={adv_section:e};this.logAdvRequest(t)}),n(e.started$,e=>{let t={adv_section:e};this.logAdvBreakStarted(t)}),n(e.ended$,({section:e,ordIds:t})=>{let n={adv_section:e,erids:t.join(`,`)};this.logAdvBreakEnded(n)}),n(e.error$,e=>{let t={error_message:e};this.logAdvError(t)}),this.adsSubscription=t,this.resubscribeLifecycleFlush(),this.subscription.add(t),t}setSessionMode(e){this.sessionMode=e,this.thinOneStatDebugLog({message:`session mode set to ${e}`})}pause(){this.logger.pause(),this.thinOneStatDebugLog({message:`paused`})}resume(){this.logger.resume(),this.thinOneStatDebugLog({message:`resumed`})}getDeviceId(){return this.deviceId}resetViewSession(){this.thinOneStatDebugLog({message:`VSID reset`}),D.generateVSID(this.config.useVsid64),this.vsid$.next(D.getVSID()),this.eventNumber=0,this.loopCount=0,this.loopLimitReached=!1,this.sentOnceEvents.clear()}async destroy(){this.thinOneStatDebugLog({message:`Destroying ThinOneStat`}),this.subscription.unsubscribe(),this.lifecycleFlushSubscription?.unsubscribe(),await this.logger.destroy(),this.apis.forEach(e=>e.destroy()),this.sentOnceEvents.clear(),this.thinOneStatDebugLog({message:`ThinOneStat destroyed`})}calcBufferTime(e){return e?Math.round(((e.end??0)-(e.start??0))*1e3):void 0}getBufferTime(e){let t=this.calcBufferTime(e.info.currentVideoBuffer$.getValue()),n=e.experimental.element$.getValue(),r=yt(n)?void 0:Gn(n);return t||r}mapErrorCategory(e){switch(e){case U.NETWORK:return`network`;case U.VIDEO_PIPELINE:return`video_pipeline`;case U.EXTERNAL_API:return`external_api`;case U.PARSER:return`parser`;case U.DOM:return`dom`;default:return`wtf`}}mapErrorSeverity(e){return U.FATAL===e?`critical`:`informative`}getCurrentContentType(){let e=or(this.player?.info.currentFormat$.getValue());if(e)return e;if(this.lastContentType)return this.lastContentType;let t=this.player?.info.availableSources$.getValue();if(!t)return;let n=Object.keys(t)[0];return or(n)}genCodecInfo(e,t){return e&&t?`${e},${t}`:void 0}getLatencyInfo(){let e=this.player?.info.atLiveEdge$.getValue();return JSON.stringify({client_time:Date.now(),live:e})}collectTelemetryData(){if(!this.player)return{};let e=this.player.experimental.element$.getValue(),t=0,n=0;e&&(t=e.getVideoPlaybackQuality().droppedVideoFrames,n=e.getVideoPlaybackQuality().totalVideoFrames);let r=this.getCurrentContentType(),i=this.player.info.currentVideoStream$.getValue()?.codec,a=this.player.info.currentAudioStream$.getValue()?.codec,o=this.player.info.videoBitrate$.getValue(),s=this.player.info.rttEstimation$.getValue(),c=this.player.info.throughputEstimation$.getValue();return{buffer_time:this.getBufferTime(this.player),dropped_frames_count:t,rendered_frames_count:n,content_type:r,user_quality:this.userQuality,playback_quality:$(this.player.info.currentQuality$.getValue()),download_quality:this.downloadQuality,codec_info:this.genCodecInfo(i,a),bandwidth_from_manifest:o?Math.round(o):void 0,cdn_host:this.cdnHostname,download_speed:c?Math.round(c):void 0,network_type:this.networkType,rtt:s?Math.round(s):void 0,latency:this.getLatencyInfo()}}getPlaybackPosition(){if(yt(this.player))return pr;if(!this.isLive){let e=vn(this.player.info.position$.getValue());return yn(e)?Math.round(e):pr}let e=this.player.info.liveTime$.getValue();return yt(e)?pr:Math.round(_n(e))}getDeviceInfo(){let e={...Cn(),...this.statContext.deviceInfo};return Object.keys(e).length?e:void 0}isValidPlatform(e=``){return[`web:desktop`,`web:mobile`,`smart_tv`].includes(e)}createRequiredParams(e){let t=this.vsid$.getValue();pt(t);let n=this.eventNumber++,r={vsid:t,uv_movie_id:this.statContext.movieId,event_name:e,client_time:Date.now(),application:this.statContext.application??`@vkontakte/videoplayer-statistics:${O}`,platform:this.isValidPlatform(this.statContext.platform)?this.statContext.platform:`web:${this.statContext.isMobile||W.device.isMobile?`mobile`:`desktop`}`,product:this.statContext.product,event_number:n,playback_position:this.getPlaybackPosition(),current_tvt:bn(this.getTotalViewTime()),cdn_host:this.cdnHostname};for(let e of this.config.disabledParams)delete r[e];return r}createRequiredFatParams(){let e={stats_version:fr,browser:this.statContext.browser??W.browser.current,browser_version:this.statContext.browserVersion??String(W.browser.currentVersion),os:this.statContext.os??W.device.os.name,os_version:this.statContext.osVersion??W.device.os.version,device_type:this.statContext.deviceType??(this.statContext.isMobile||W.device.isMobile?`mobile`:`desktop`),device_manufacturer:this.statContext.deviceManufacturer??W.device.details.vendor,device_model:this.statContext.deviceModel??W.device.details.model,device_info:this.statContext.deviceInfo??this.getDeviceInfo(),navigation:this.statContext.navigation};for(let t of this.config.disabledParams)delete e[t];return e}log(e,t,n=!1){if(this.sessionMode===`adv`&&!cn(e)||this.config.disabledEvents.includes(e)||this.loopLimitReached&&!kt.includes(e))return;if(this.onceEvents.has(e)){if(this.sentOnceEvents.has(e))return;this.sentOnceEvents.add(e)}let r=this.createRequiredParams(e),i={};n&&(i=this.createRequiredFatParams()),this.logger.log({...r,...i,...t})}logWatchCoverage(e){this.log(`watch_coverage`,e,!0)}logWatchCoverageLive(e){this.log(`watch_coverage_live`,e,!0)}logWatchedN(e){this.log(`watched_n`,e,!0)}logStartSession(e){this.log(`start_session`,e,!0)}logPreloadStarted(e){this.log(`preload_started`,e)}logPreloadEnded(e){this.log(`preload_ended`,e)}logPreloadError(){this.log(`preload_error`)}logPlaybackStarted(e){this.log(`playback_started`,e,!0)}logPlay(){this.log(`play`)}logManifestRequest(e){this.log(`manifest_request`,e)}logFirstByteManifest(e){this.log(`first_byte_manifest`,e)}logFirstMediaRequest(e){this.log(`first_media_request`,e)}logFirstByteMedia(e){this.log(`first_byte_media`,e)}logManifestReceived(e){this.log(`manifest_received`,e)}logConnectionEstablished(e){this.log(`connection_established`,e)}logFirstVideoFrameDecoded(){this.log(`first_video_frame_decoded`)}logReady(e){this.log(`ready`,e)}logPlaying(e){this.log(`playing`,e)}logPause(){this.log(`pause`)}logResume(){this.log(`resume`)}logSeeking(e){this.log(`seeking`,e)}logSeeked(e){this.log(`seeked`,e)}logContentEndReached(){this.log(`content_end_reached`)}logStop(){this.log(`stop`)}logBufferStarvation(e){this.log(`buffer_starvation`,e)}logShowLoader(){this.log(`show_loader`)}logQualityChangeRequested(e){this.log(`quality_change_requested`,e)}logQualityChanged(e){this.log(`quality_changed`,e)}logCdnHostChanged(e){this.log(`cdn_host_changed`,e)}logViewPortChanged(e){this.log(`view_port_changed`,e)}logModeChanged(e){this.log(`mode_changed`,e)}logVisibilityChanged(e){this.log(`visibility_changed`,e)}logAudioTrackSwitched(e){this.log(`audio_track_switched`,e)}logVideoTrackSwitched(e){this.log(`video_track_switched`,e)}logSubtitlesSwitched(e){this.log(`subtitles_switched`,e)}logSoundChanged(e){this.log(`sound_changed`,e)}logFailover(e){this.log(`failover`,e)}logContentTypeChange(e){this.log(`content_type_change`,e)}logError(e){this.log(`error`,e,!0)}logCannotPlay(){this.log(`cannot_play`,{},!0)}logDownloadBytes(e){this.log(`download_bytes`,e)}logTelemetry(e){this.log(`telemetry`,e)}logAdvConfiguration(e){this.log(`adv_configuration`,e)}logAdvRequest(e){this.log(`adv_request`,e)}logAdvBreakStarted(e){this.log(`adv_break_started`,e)}logAdvBreakEnded(e){this.log(`adv_break_ended`,e)}logAdvError(e){this.log(`adv_error`,e)}resubscribeLifecycleFlush(){this.lifecycleFlushSubscription?.unsubscribe();let e=wt(),t=()=>{this.logger.flush({wantBeacon:!0,clearStorage:this.config.clearStorageAtUnload}).catch(()=>{})},n=new H;n.add(ht(e,`beforeunload`).subscribe(t)),n.add(ht(e,`pagehide`).subscribe(t)),n.add(ht(e.document,`visibilitychange`).subscribe(e=>{e.target?.visibilityState===`hidden`&&t()})),this.lifecycleFlushSubscription=n}}import{PlaybackState as gr,Subscription as _r}from"@vkontakte/videoplayer-core/evergreen";import{Logger as vr}from"@vkontakte/videoplayer-shared/evergreen";class yr{startTime=null;stopTime=null;accumulatedTime=0;get time(){return Math.floor(this.timeMs/1e3)}get timeMs(){return this.startTime===null?0:(this.stopTime??this.now())-this.startTime-this.accumulatedTime}get isRunning(){return this.startTime!==null&&this.stopTime===null}start(){this.isRunning||(this.stopTime===null?this.startTime=this.now():(this.accumulatedTime+=this.now()-this.stopTime,this.stopTime=null))}stop(){this.isRunning&&(this.stopTime=this.now())}reset(){this.startTime=null,this.stopTime=null,this.accumulatedTime=0}now(){return Date.now()}}var br=()=>window.Image?new Image:document.createElement(`img`);class xr{params;position=0;started=!1;isActiveLive;heartbeatPixels=[];heartbeatLastTimeSent={};heartbeatInterval;heartbeatFirstTimeoutId;idleCallbackIds=[];log;stopwatch=new yr;subscription=new _r;constructor(e){this.params=e;let t=new vr;this.log=t.createComponentLog(`MediascopePixel`)}attachTo(e){this.subscription.add(e.info.playbackState$.subscribe(e=>this.onPlaybackState(e))).add(e.info.position$.subscribe(e=>this.onPosition(e))).add(e.info.atLiveEdge$.subscribe(e=>this.isActiveLive=e)),this.subscription.add(e.events.willSeek$.subscribe(()=>this.onWillSeek())).add(e.events.seeked$.subscribe(()=>this.onSeeked())).add(e.events.ended$.subscribe(()=>this.onEnded()))}destroy(){this.stopwatch.reset(),this.subscription.unsubscribe(),A.clearTimeout(this.heartbeatFirstTimeoutId),this.idleCallbackIds.forEach(e=>window.cancelIdleCallback(e)),this.send(`stop`)}onPlaybackState(e){e===gr.PLAYING?this.started?this.play():this.start():e===gr.PAUSED&&this.pause()}start(){this.started=!0,this.stopwatch.start(),this.send(`start`),this.heartbeatPixels=this.prepareHeartbeatPixels(),this.heartbeatInterval=this.heartbeatPixels[0]?.interval;let e=this.heartbeatPixels[0]?.url,t=this.heartbeatPixels[0]?.delay;if(t!==void 0&&e)try{this.heartbeatFirstTimeoutId=A.setTimeout(()=>{this.sendHeartbeat(e)},t*1e3)}catch(e){this.log({message:e.message})}}play(){this.stopwatch.start(),this.send(`resume`)}pause(){this.stopwatch.stop(),this.send(`pause`)}onPosition(e){if(this.position=e,this.heartbeatInterval&&this.stopwatch.time!==0&&this.stopwatch.time%this.heartbeatInterval===0)for(let[e,{url:t}]of this.heartbeatPixels.entries())this.heartbeatLastTimeSent[e]!==this.stopwatch.time&&(this.heartbeatLastTimeSent[e]=this.stopwatch.time,this.sendHeartbeat(t))}onWillSeek(){this.started&&this.send(`stop`)}onSeeked(){this.started&&this.send(`start`)}onEnded(){this.stopwatch.stop(),this.send(`stop`)}validatePixels(e,t){return e?.filter(e=>e.event===t.event&&t.keys.every(t=>!!e[t]))}getFrameTimestamp(){let e;if(this.isActiveLive){let t=Math.floor(Math.random()*25)+5;e=Date.now()/1e3-t}else e=this.position||0;return Math.floor(e)}getUTC(){return Math.floor(Date.now()/1e3)}prepareUrl(e){return e.replace(`{@fts_fake_sec}`,String(this.getFrameTimestamp())).replace(`{@utc_sec}`,String(this.getUTC())).replace(`{@vsid}`,D.getVSID()).split(`&`).filter(e=>!/={@[a-zA-Z_]+}/.test(e)).join(`&`)}preparePixels(e){return this.validatePixels(this.params.pixels,{event:e,keys:[`url`]})}prepareHeartbeatPixels(){return this.validatePixels(this.params.pixels,{event:`heartbeat`,keys:[`url`,`interval`]})}call(e){try{this.idleCallbackIds.push(requestIdleCallback(()=>br().src=this.prepareUrl(e)))}catch(e){this.log({message:e.message})}}send(e){let t=this.preparePixels(e);for(let{url:e}of t)this.call(e)}sendHeartbeat(e){this.call(e)}}export{O as VERSION,ft as ThinOneStat,l as SurfaceMode,u as SessionMode,d as SeekAction,o as Quality,dt as OneStat,xr as MediascopePixel,f as InteractiveInterfaceClick,s as ContentType,c as ConnectionType,a as ApiEnv};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vkontakte/videoplayer-statistics",
3
- "version": "1.0.119-dev.ebda080eb.0",
3
+ "version": "1.0.120-beta.0",
4
4
  "author": "vk.ru",
5
5
  "description": "Statistics library for vk.ru videoplayer",
6
6
  "homepage": "https://vk.ru",
@@ -42,7 +42,7 @@
42
42
  "**/*.d.ts"
43
43
  ],
44
44
  "dependencies": {
45
- "@vkontakte/videoplayer-core": "2.0.173-dev.ebda080eb.0",
46
- "@vkontakte/videoplayer-shared": "1.0.102-dev.ebda080eb.0"
45
+ "@vkontakte/videoplayer-core": "2.0.174-beta.0",
46
+ "@vkontakte/videoplayer-shared": "1.0.103-beta.0"
47
47
  }
48
48
  }
@@ -63,6 +63,7 @@ export interface IStatContext {
63
63
  recomInfo?: Record<string, unknown>;
64
64
  product?: string;
65
65
  navigation?: Record<string, unknown>;
66
+ deviceInfo?: Record<string, unknown>;
66
67
  application?: string;
67
68
  platform?: string;
68
69
  os?: string;
@@ -106,7 +107,6 @@ export interface IWatchCoverageLivePayload extends IWatchCoveragePayload {
106
107
  }
107
108
  export interface IWatchedNPayload {
108
109
  target_duration: Milliseconds;
109
- current_tvt: Milliseconds;
110
110
  }
111
111
  export interface IStartSessionPayload {
112
112
  isid?: string;
@@ -348,7 +348,6 @@ export declare class ThinOneStat implements IThinOneStat {
348
348
  private currentSubtitle?;
349
349
  private userQuality?;
350
350
  private downloadQuality?;
351
- private downloadSpeed?;
352
351
  private networkType?;
353
352
  private uiEvents?;
354
353
  private playerSubscription?;
@@ -373,6 +372,7 @@ export declare class ThinOneStat implements IThinOneStat {
373
372
  resetViewSession(): void;
374
373
  destroy(): Promise<void>;
375
374
  private calcBufferTime;
375
+ private getBufferTime;
376
376
  private mapErrorCategory;
377
377
  private mapErrorSeverity;
378
378
  private getCurrentContentType;
@@ -380,6 +380,7 @@ export declare class ThinOneStat implements IThinOneStat {
380
380
  private getLatencyInfo;
381
381
  private collectTelemetryData;
382
382
  private getPlaybackPosition;
383
+ private getDeviceInfo;
383
384
  private isValidPlatform;
384
385
  private createRequiredParams;
385
386
  private createRequiredFatParams;
@@ -1,7 +1,7 @@
1
1
  import type { IPlayer, IPlayerInfo, IPlayerEvents } from "@vkontakte/videoplayer-core";
2
2
  export interface IThinOneStatPlayerAdapter {
3
- info: Pick<IPlayerInfo, "atLiveEdge$" | "availableSources$" | "availableTextTracks$" | "currentAudioStream$" | "currentVideoBuffer$" | "currentFormat$" | "currentPlaybackRate$" | "currentQuality$" | "currentTextTrack$" | "currentVideoStream$" | "hostname$" | "httpConnectionMetrics$" | "httpConnectionReused$" | "httpConnectionType$" | "httpDownloadMetrics$" | "isAutoQualityEnabled$" | "isLive$" | "isStalled$" | "liveTime$" | "muted$" | "playbackState$" | "position$" | "rttEstimation$" | "stallStartTime$" | "surface$" | "videoBitrate$" | "volume$">;
4
- events: Pick<IPlayerEvents, "willStart$" | "willResume$" | "willReady$" | "willSeek$" | "willStop$" | "willDestruct$" | "started$" | "playing$" | "paused$" | "seeked$" | "ended$" | "looped$" | "firstBytes$" | "firstBytesManifest$" | "firstBytesRequested$" | "firstFrame$" | "manifestRequested$" | "manifestReceived$" | "managedError$" | "fatalError$" | "fetcherRecoverableError$">;
3
+ info: Pick<IPlayerInfo, "atLiveEdge$" | "availableSources$" | "availableTextTracks$" | "currentAudioStream$" | "currentVideoBuffer$" | "currentFormat$" | "currentPlaybackRate$" | "currentQuality$" | "currentTextTrack$" | "currentVideoStream$" | "hostname$" | "httpConnectionMetrics$" | "httpConnectionReused$" | "httpConnectionType$" | "httpDownloadMetrics$" | "isAutoQualityEnabled$" | "isLive$" | "isStalled$" | "liveTime$" | "muted$" | "playbackState$" | "position$" | "rttEstimation$" | "stallStartTime$" | "surface$" | "throughputEstimation$" | "videoBitrate$" | "volume$">;
4
+ events: Pick<IPlayerEvents, "willStart$" | "willResume$" | "willReady$" | "willSeek$" | "willStop$" | "willDestruct$" | "started$" | "playing$" | "paused$" | "seeked$" | "ended$" | "looped$" | "firstBytes$" | "firstBytesManifest$" | "firstBytesRequested$" | "firstFrame$" | "canplay$" | "manifestRequested$" | "manifestReceived$" | "managedError$" | "fatalError$" | "fetcherRecoverableError$">;
5
5
  experimental: Pick<IPlayer["experimental"], "element$">;
6
6
  getExactTime: IPlayer["getExactTime"];
7
7
  getExactLiveTime: IPlayer["getExactLiveTime"];
@@ -22,6 +22,7 @@ export interface IRequiredFatParams {
22
22
  device_type: string;
23
23
  device_manufacturer: string;
24
24
  device_model: string;
25
+ device_info?: Record<string, unknown>;
25
26
  navigation?: Record<string, unknown>;
26
27
  }
27
28
  export type ILogItem = IRequiredParams & Partial<IRequiredFatParams>;
@@ -0,0 +1,2 @@
1
+ import type { Milliseconds } from "@vkontakte/videoplayer-shared";
2
+ export declare const getBufferAhead: ({ buffered, currentTime }: Pick<HTMLMediaElement, "buffered" | "currentTime">) => Milliseconds | undefined;
@@ -0,0 +1 @@
1
+ export declare const collectDeviceInfo: () => Record<string, unknown>;
@@ -3,3 +3,4 @@ export declare const isLiveTime: (time: Milliseconds | undefined) => time is Mil
3
3
  export declare const getLiveTime: (liveTime: Milliseconds | undefined) => Milliseconds;
4
4
  export declare const toMilliseconds: (seconds: Seconds | undefined) => Milliseconds;
5
5
  export declare const isValidPosition: (position: Milliseconds) => boolean;
6
+ export declare const clampTotalViewTime: (totalViewTime: Milliseconds) => Milliseconds;