@vkontakte/videoplayer-statistics 1.0.34 → 1.0.35-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.34
3
- * Mon, 17 Apr 2023 11:31:09 GMT
4
- * https://st.mycdn.me/static/vkontakte-videoplayer/1-0-34/doc/
2
+ * @vkontakte/videoplayer-statistics v1.0.35-beta.0
3
+ * Wed, 24 May 2023 14:18:45 GMT
4
+ * https://st.mycdn.me/static/vkontakte-videoplayer/1-0-35/doc/
5
5
  */
6
- import{ValueSubject as $,getExponentialDelay as B,ErrorCategory as u,now as A,assertNever as U,Subscription as v,Subject as L,fromEvent as H,isNonNullable as k}from"@vkontakte/videoplayer-shared/esnext.esm.js";import{VideoQuality as c,HttpConnectionType as y,VideoFormat as n,ChromecastState as C}from"@vkontakte/videoplayer-core/esnext.esm.js";import D from"lodash/debounce.js";const w="_one-stat_",m=(()=>{try{const t=Date.now().toString(),e=window.localStorage;e.setItem(t,t);const i=e.getItem(t)===t;return e.removeItem(t),i?e:null}catch{return null}})();function M(t){const e=m?m.getItem(w+t):null;if(e===null)return null;try{return JSON.parse(e)}catch{return null}}function q(t,e){try{m&&m.setItem(w+t,JSON.stringify(e))}catch{}}function K(t){m&&m.removeItem(w+t)}var N={get(t){return M(t)||null},set(t,e){q(t,e)},remove(t){K(t)}};function z(){const t="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),e=new Array(36);let i=0,s,o;for(o=0;o<36;o++)o===8||o===13||o===18||o===23?e[o]="-":o===14?e[o]="4":(i<=2&&(i=33554432+Math.random()*16777216|0),s=i&15,i=i>>4,e[o]=t[o===19?s&3|8:s]);return e.join("")}var l;(function(t){t.PROD="prod",t.VK_ALIAS="vk_alias",t.VIDEOTEST="videotest",t.TEST="test",t.AUTO="auto"})(l||(l={}));var h;(function(t){t.Q144P="144",t.Q240P="240",t.Q360P="360",t.Q480P="480",t.Q720P="720",t.Q1080P="1080",t.Q1440P="1440",t.Q2160P="2160",t.Q4320P="4320",t.UNKNOWN="unknown"})(h||(h={}));var P;(function(t){t.CHROMECAST="chromecast",t.APPLE_TV="appletv",t.PIP="pip",t.HEADPHONES="externalHeadphones",t.SCREEN=""})(P||(P={}));var d;(function(t){t.MP4="mp4",t.DASH="dash",t.DASH_SEP="dash_sep",t.ONDEMAND_DASH="ondemand_dash",t.HLS="hls",t.ONDEMAND_HLS="ondemand_hls",t.WEBM="webm",t.RTMP="rtmp",t.UNKNOWN="unknown"})(d||(d={}));var T;(function(t){t.AUTO="auto",t.MANUAL=""})(T||(T={}));var _;(function(t){t.HTTP1="http1",t.HTTP2="http2",t.HTTP3="http3"})(_||(_={}));var E;(function(t){t[t.YES=1]="YES",t[t.NO=0]="NO"})(E||(E={}));const I="1.0.34",Q="evergreen.esm.js",W="CIOPGQJGDIHBABABA",O={[l.PROD]:"https://api.ok.ru",[l.VK_ALIAS]:"https://api.mycdn.me",[l.VIDEOTEST]:"https://videotestapi.ok.ru/api",[l.TEST]:"https://apitest.ok.ru",[l.AUTO]:""},F={start:1e3,factor:1.5,max:5*60*1e3,random:.1};var p;(function(t){t[t.PARAM_SESSION_EXPIRED=102]="PARAM_SESSION_EXPIRED",t[t.PARAM_SESSION_KEY=103]="PARAM_SESSION_KEY",t[t.PARAM_SIGNATURE=104]="PARAM_SIGNATURE",t[t.AUTH_LOGIN=401]="AUTH_LOGIN"})(p||(p={}));class V{params;uuid;authToken;sessionKey;authorizePromise;refreshAuthTokenPromise;consequentAuthErrors=0;authorized$=new $(!1);backoffTimeoutId;constructor(e){this.params=e;const i=N.get("uuid");i?this.uuid=String(i):(this.uuid=z(),N.set("uuid",this.uuid))}async authorize(e){return this.authToken=e??await this.refreshAuthToken(),this._authorizeWithBackoff()}logBeacon(e){const i="log.externalLog",s=this.createLogParams(e),o=this.sessionKey;if(!o)return;this.params.apiTransport.sendBeacon(i,s,o)||this.logRequest(e)}async logRequest(e){const i="log.externalLog",s=this.createLogParams(e),o=this.sessionKey??await this._authorizeWithBackoff();if(!o)return;const a=async(r,g=this.params.requestRetryCount)=>{try{return await this.params.apiTransport.sendRequest(i,s,o)}catch(f){if(!f||!("error_code"in f)){this.params.error$.next({id:"logRequestUnknown",category:u.NETWORK,message:`Unknown ${i} error`,thrown:f});return}const S=f?.error_code;switch(S){case p.PARAM_SESSION_EXPIRED:case p.PARAM_SESSION_KEY:case p.PARAM_SIGNATURE:return this.authorized$.next(!1),this.sessionKey=await this._authorizeWithBackoff(),g>0?a(r,g-1):void 0;case p.AUTH_LOGIN:return this.authorized$.next(!1),this.authToken=await this.refreshAuthToken(),this.sessionKey=await this._authorizeWithBackoff(),g>0?a(r,g-1):void 0;default:{this.params.error$.next({id:`LogRequest#${S}`,category:u.EXTERNAL_API,message:`${i} error`,data:f});return}}}};return a(e)}destroy(){window.clearTimeout(this.backoffTimeoutId),this.backoffTimeoutId=0}async refreshAuthToken(){if(this.params.refreshAuthToken)return this.refreshAuthTokenPromise||(this.refreshAuthTokenPromise=this.params.refreshAuthToken().finally(()=>{this.refreshAuthTokenPromise=void 0})),this.refreshAuthTokenPromise}createLogParams(e){return{collector:"ok.mobile.apps.video",data:JSON.stringify({application:`${Q}:${I}`,platform:"WEB",items:e.map(i=>({...i,time:0,type:1}))})}}_authorizeWithBackoff(){if(!this.consequentAuthErrors)return this._authorize();const e=B(this.consequentAuthErrors,F);return new Promise(i=>{this.backoffTimeoutId||(this.backoffTimeoutId=window.setTimeout(()=>{this._authorize().then(i).catch(s=>this.params.error$.next({id:"AuthorizeBackoff",category:u.NETWORK,message:"Otherwise unhandle errror in authrization",thrown:s})).finally(()=>this.backoffTimeoutId=0)},e))})}async _authorize(){if(this.authorizePromise)return this.authorizePromise;this.sessionKey=void 0,this.authorized$.next(!1);const e="auth.anonymLogin",i={session_data:{version:2,device_id:this.uuid,client_version:I.split("-")[0],client_type:"SDK_JS"}};return this.authToken!==void 0&&(i.session_data.auth_token=this.authToken,i.session_data.version=3),this.authorizePromise=this.params.apiTransport.sendRequest(e,i).then(s=>((!s||!s.session_key)&&this.params.error$.next({id:"AuthorizeFailed",category:u.EXTERNAL_API,message:"No session key",data:s}),this.sessionKey=s?.session_key??void 0,this.sessionKey)).catch(async s=>{this.sessionKey=void 0;const o=s?.error_code;switch(o){case p.AUTH_LOGIN:return this.authToken=await this.refreshAuthToken(),this._authorizeWithBackoff()}o?this.params.error$.next({id:`Authorize#${o}`,category:u.EXTERNAL_API,message:"authorize error",data:s}):this.params.error$.next({id:"AuthorizeUnknown",category:u.NETWORK,message:"authorize error",thrown:s})}).finally(()=>{this.authorizePromise=void 0,this.consequentAuthErrors=this.sessionKey?0:this.consequentAuthErrors+1,this.authorized$.next(this.sessionKey!==void 0)}),this.authorizePromise}}class G{apiKey;apiBaseUrl;apiEnv;timeSynchronisation;isApiBaseUrlFetched;params;constructor(e){this.params=e,this.apiKey=e.apiKey,this.apiEnv=e.apiEnv,this.apiBaseUrl=e.apiBaseUrl,this.timeSynchronisation=e.timeSynchronisation,this.isApiBaseUrlFetched=!1}async resolveApiBaseUrl(){if(this.apiEnv!==l.AUTO||this.isApiBaseUrlFetched)return this.apiBaseUrl;try{const e=atob("aHR0cHM6Ly9kbnMuZ29vZ2xlL3Jlc29sdmU/bmFtZT12aWRlby5fZW5kcG9pbnQub2sucnUmdHlwZT1UWFQ="),o=(await(await fetch(e,{method:"GET",mode:"cors",cache:"no-cache"})).json())?.Answer[0]?.data;if(!o)throw new Error("Wrong DNS response");return o}catch(e){return this.params.error$.next({id:"OneStat:ApiTransport:resolveApiBaseUrl",category:u.NETWORK,message:"Unhandled resolve api base url error",thrown:e}),O[l.VK_ALIAS]}finally{this.isApiBaseUrlFetched=!0}}sendBeacon(e,i,s){if(!window.Blob||!window.navigator.sendBeacon)return!1;const o=this._prepareQueryParams({method:e,queryParams:i,sessionKey:s}),a=new window.Blob([o.toString()],{type:"application/x-www-form-urlencoded"});try{return window.navigator.sendBeacon(`${this.apiBaseUrl}/fb.do`,a)}catch(r){this.params.error$.next({id:"OneStat:ApiTransport:sendBeacon",category:u.NETWORK,message:"Unhandled beacon error",thrown:r,data:{method:e,params:i}})}return!1}async sendRequest(e,i,s){const o=A(),a=r=>{if(r instanceof TypeError&&["Failed to fetch","NetworkError when attempting to fetch resource."].includes(r.message)){this.params.error$.next({id:"Network",category:u.NETWORK,message:"Request failed",thrown:r});return}this.params.error$.next({id:"OneStat:ApiTransport:sendRequest",category:u.NETWORK,message:"Unhandled request error",thrown:r,data:{method:e,params:i,sessionKey:s,time:A()-o}})};return this.apiBaseUrl=await this.resolveApiBaseUrl(),fetch(`${this.apiBaseUrl}/fb.do`,{method:"post",headers:{"Content-type":"application/x-www-form-urlencoded"},body:this._prepareQueryParams({method:e,queryParams:i,sessionKey:s})}).then(r=>{const g=Number(r.headers.get("content-length"))===0,f=new Date(r.headers.get("date")??"").getTime(),S=A()-o;if(isFinite(f)&&this.timeSynchronisation.addServerTime(f,S),!g)return r.json().then(b=>Object.prototype.hasOwnProperty.call(b,"error_msg")?Promise.reject(b):b,a)},a)}_prepareQueryParams(e){const i=new URLSearchParams({format:"JSON",method:e.method,application_key:this.apiKey});return e.sessionKey!==void 0&&i.append("session_key",e.sessionKey),Object.entries(e.queryParams).forEach(([s,o])=>i.append(s,typeof o=="string"?o:JSON.stringify(o))),i}}const x=3e3,j=x*5;class J{api;error$;requestQueue=[];subscription;isPaused=!1;debouncedFlush=D(()=>{try{this.flush()}catch(e){this.error$.next({id:"LoggerError",category:u.WTF,message:String(e)??"Unknown logger error",thrown:e})}},x,{maxWait:j});constructor(e){this.api=e.api,this.error$=e.error$,this.subscription=this.api.authorized$.subscribe(i=>{i&&this.debouncedFlush()})}log(e){this.requestQueue.push(e),this.isPaused||this.debouncedFlush()}flush(e=!1){this.requestQueue.length!==0&&(this.api.authorized$.getValue()?(e?this.api.logBeacon(this.requestQueue):this.api.logRequest(this.requestQueue),this.requestQueue=[]):this.api.authorize(),this.debouncedFlush.cancel())}pause(){this.isPaused=!0,this.debouncedFlush.cancel()}resume(){this.isPaused=!1,this.debouncedFlush()}destroy(){this.subscription.unsubscribe()}}class X{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,i){const s=Date.now(),o=e-s-i/2;if(Math.abs(o)<1e3){this.offset=0;return}this.offset===void 0?this.offset=Math.round(o):this.offset=Math.round(.2*o+(1-.2)*this.offset)}}const R=()=>Math.floor(Math.random()*2**32).toString(36).padStart(8,"0"),Y=t=>t&&{[c.INVARIANT]:h.UNKNOWN,[c.Q_144P]:h.Q144P,[c.Q_240P]:h.Q240P,[c.Q_360P]:h.Q360P,[c.Q_480P]:h.Q480P,[c.Q_720P]:h.Q720P,[c.Q_1080P]:h.Q1080P,[c.Q_1440P]:h.Q1440P,[c.Q_2160P]:h.Q2160P,[c.Q_4320P]:h.Q4320P}[t],Z=t=>t&&{[y.HTTP1]:_.HTTP1,[y.HTTP2]:_.HTTP2,[y.QUIC]:_.HTTP3}[t],ee=t=>{if(t!==void 0)switch(t){case n.MPEG:return d.MP4;case n.DASH:case n.DASH_LIVE:case n.DASH_LIVE_CMAF:return d.DASH;case n.DASH_SEP:return d.DASH_SEP;case n.DASH_ONDEMAND:return d.ONDEMAND_DASH;case n.DASH_WEBM:case n.DASH_LIVE_WEBM:return d.WEBM;case n.HLS:case n.HLS_LIVE:case n.HLS_LIVE_CMAF:return d.HLS;case n.HLS_ONDEMAND:return d.ONDEMAND_HLS;case n.WEB_RTC_LIVE:return d.RTMP;default:return U(t)}};class oe{api;logger;config;subscription=new v;timeSynchronisation=new X;experimental;statContext;vsid=R();fieldBlacklist=[];constructor(e,i){this.statContext=e,this.config={...i.config??{},requestRetryCount:1};const s=new L;this.experimental={error$:s};const o=new G({apiKey:W,apiBaseUrl:O[i.apiEnv],apiEnv:i.apiEnv,error$:s,timeSynchronisation:this.timeSynchronisation});this.api=new V({apiTransport:o,refreshAuthToken:i.refreshAuthToken,requestRetryCount:this.config.requestRetryCount,error$:s}),this.logger=new J({api:this.api,error$:s}),this.subscription.add(H(window,"beforeunload").subscribe(()=>this.logger.flush(!0)))}updateContext(e){this.statContext={...this.statContext,...e}}attachTo(e){let i;return new v().add(e.events.started$.subscribe(()=>{this.logStarted({position:e.info.position$.getValue()})})).add(e.events.looped$.subscribe(()=>{this.resetViewSession(),this.logStarted({position:e.info.position$.getValue()})})).add(e.events.seeked$.subscribe(()=>{this.logSeek()})).add(e.events.willPause$.subscribe(()=>{this.logPause({position:e.info.position$.getValue()})})).add(e.events.willResume$.subscribe(()=>{this.logPlay()})).add(e.events.managedError$.subscribe(({id:s})=>{this.logError({errorType:s})})).add(e.events.fatalError$.subscribe(({id:s})=>{this.logError({errorType:s})})).add(e.events.inited$.subscribe(()=>{this.logInited()})).add(e.events.canplay$.subscribe(s=>{this.logReady({time:s})})).add(e.events.firstBytes$.subscribe(s=>{this.logFirstBytes({time:s})})).add(e.events.firstFrame$.subscribe(s=>{this.logFirstFrame({time:s})})).add(e.events.watchCoverageRecord$.subscribe(s=>{this.logWatchCoverageRecord(s)})).add(e.events.watchCoverageLive$.subscribe(s=>{this.logWatchCoverageLive(s)})).add(e.info.isStalled$.subscribe(s=>{s?i=A():(k(i)&&this.logEmptyBuffer({duration:A()-i}),i=void 0)})).add(e.events.ended$.subscribe(()=>{const s=e.info.throughputEstimation$.getValue();k(s)&&this.logDownloadSpeed({speed:s/8})})).add(e.info.hostname$.subscribe(s=>{this.updateContext({cdnHostname:s})})).add(e.info.httpConnectionType$.subscribe(s=>{this.updateContext({connectionType:Z(s)})})).add(e.info.httpConnectionReused$.subscribe(s=>{this.updateContext({connectionReused:s})})).add(e.info.currentQuality$.subscribe(s=>{this.updateContext({quality:Y(s)})})).add(e.info.currentFormat$.subscribe(s=>{this.updateContext({contentType:ee(s)})})).add(e.info.chromecastState$.subscribe(s=>{this.updateContext({destination:s===C.CONNECTED?P.CHROMECAST:P.SCREEN})}))}authorize(e){return this.api.authorize(e)}pause(){this.logger.pause()}resume(){this.logger.resume()}destroy(){this.logger.flush(),this.subscription.unsubscribe(),this.api.destroy(),this.logger.destroy()}resetViewSession(){this.vsid=R()}setFieldBlacklist(e=[]){this.fieldBlacklist=e}logInited(){this.log({operation:"init"})}logReady(e){this.log({operation:"player_ready",param:String(e.time)})}logStarted(e){this.log({operation:"play",param:String(Math.round(e.position))})}logPlay(){this.log({operation:"play_toggle"})}logPause(e){this.log({operation:"pause",param:String(Math.round(e.position))})}logSeek(){this.log({operation:"seek"})}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:"error",param:e.errorType})}logWatchCoverageRecord(e){this.log({operation:"watch_coverage_record",param:`${e.start}-${e.end}`}),this.logger.flush(!0)}logWatchCoverageLive(e){const i=e.start+this.timeSynchronisation.getOffset(),s=e.end+this.timeSynchronisation.getOffset();this.log({operation:"watch_coverage_live",param:`${i}-${s}`}),this.logger.flush(!0)}logEmptyBuffer(e){this.log({operation:"empty_buffer",param:String(e.duration)})}logDownloadSpeed(e){this.log({operation:"download_speed",param:String(e.speed)})}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"})}log(e){const i=this.createLogItem(e);this.logger.log(i)}createLogItem({operation:e,param:i}){const s=this.timeSynchronisation.now(),o={vsid:this.vsid,vid:this.statContext.movieId,quality:this.statContext.quality,dst:this.statContext.destination,ct:this.statContext.contentType,aid:this.statContext.albumId,place:this.statContext.place,cdn_host:this.statContext.cdnHostname,connection_type:this.statContext.connectionType,connection_reused:this.statContext.connectionReused===!0?E.YES:this.statContext.connectionReused===!1?E.NO:void 0,stat_type:this.statContext.autoplay===!0?T.AUTO:this.statContext.autoplay===!1?T.MANUAL:void 0,vk_playlist_id:this.statContext.vkPlaylistId,vk_app_id:this.statContext.vkAppId,track_code:this.statContext.trackCode};i!==void 0&&(o.param=i);for(const a of this.fieldBlacklist)delete o[a];return{operation:e,timestamp:s,custom:o}}}export{l as ApiEnv,_ as ConnectionType,d as ContentType,oe as OneStat,h as Quality,P as ScreenDestination};
6
+ import{ValueSubject as H,safeStorage as w,getExponentialDelay as J,ErrorCategory as g,now as f,assertNever as X,Observable as Z,Subscription as $,merge as k,fromEvent as B,filterChanged as tt,map as et,filter as M,isNullable as F,Subject as st,once as it,combine as W,observableFrom as ot,isNonNullable as v}from"@vkontakte/videoplayer-shared/esnext.esm.js";import K from"lodash/debounce.js";import{Surface as N,VideoQuality as E,HttpConnectionType as L,VideoFormat as c,PlaybackState as V}from"@vkontakte/videoplayer-core/esnext.esm.js";function nt(){const e="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),t=new Array(36);let s=0,i,o;for(o=0;o<36;o++)o===8||o===13||o===18||o===23?t[o]="-":o===14?t[o]="4":(s<=2&&(s=33554432+Math.random()*16777216|0),i=s&15,s=s>>4,t[o]=e[o===19?i&3|8:i]);return t.join("")}var b;(function(e){e.PROD="prod",e.VK_ALIAS="vk_alias",e.VIDEOTEST="videotest",e.TEST="test",e.AUTO="auto"})(b||(b={}));var d;(function(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"})(d||(d={}));var l;(function(e){e.MP4="mp4",e.DASH="dash",e.DASH_SEP="dash_sep",e.ONDEMAND_DASH="ondemand_dash",e.HLS="hls",e.ONDEMAND_HLS="ondemand_hls",e.WEBM="webm",e.ONDEMAND_DASH_LIVE="ondemand_dash_live",e.ONDEMAND_HLS_LIVE="ondemand_hls_live",e.WEBRTC="webrtc",e.UNKNOWN="unknown",e.RTMP="rtmp"})(l||(l={}));var R;(function(e){e.AUTO="auto",e.MANUAL=""})(R||(R={}));var x;(function(e){e.HTTP1="http1",e.HTTP2="http2",e.HTTP3="http3"})(x||(x={}));var _;(function(e){e[e.YES=1]="YES",e[e.NO=0]="NO"})(_||(_={}));var y;(function(e){e.EXCELLENT="excellent",e.GOOD="good",e.POOR="poor"})(y||(y={}));var P;(function(e){e.PIP="pip",e.FULLSCREEN="fullscreen",e.EXTERNAL="external",e.PREFETCH="prefetch"})(P||(P={}));var I;(function(e){e.SLIDER="slider",e.DOUBLE_TAP="double_tap",e.TIME_CODE="time_code",e.EPISODE="episode",e.REWIND="rewind",e.LIVE="live",e.UNKNOWN="unknown"})(I||(I={}));const q="1.0.35-beta.0",rt="CIOPGQJGDIHBABABA",j={[b.PROD]:"https://api.ok.ru",[b.VK_ALIAS]:"https://api.mycdn.me",[b.VIDEOTEST]:"https://videotestapi.ok.ru/api",[b.TEST]:"https://apitest.ok.ru",[b.AUTO]:""},at="_one-stat_",z=`${at}uuid`,ht={start:1e3,factor:1.5,max:5*60*1e3,random:.1};var T;(function(e){e[e.PARAM_SESSION_EXPIRED=102]="PARAM_SESSION_EXPIRED",e[e.PARAM_SESSION_KEY=103]="PARAM_SESSION_KEY",e[e.PARAM_SIGNATURE=104]="PARAM_SIGNATURE",e[e.AUTH_LOGIN=401]="AUTH_LOGIN"})(T||(T={}));class ut{params;uuid;authToken;sessionKey;authorizePromise;refreshAuthTokenPromise;consequentAuthErrors=0;authorized$=new H(!1);backoffTimeoutId;constructor(t){this.params=t;const s=w.get(z);s?this.uuid=String(s):(this.uuid=nt(),w.set(z,this.uuid))}async authorize(t){return this.authToken=t??await this.refreshAuthToken(),this._authorizeWithBackoff()}logBeacon(t){const s="log.externalLog",i=this.createLogParams(t),o=this.sessionKey;if(!o)return;this.params.apiTransport.sendBeacon(s,i,o)||this.logRequest(t)}async logRequest(t){const s="log.externalLog",i=this.createLogParams(t),o=this.sessionKey??await this._authorizeWithBackoff();if(!o)return;const a=async(r,u=this.params.requestRetryCount)=>{try{return await this.params.apiTransport.sendRequest(s,i,o)}catch(h){if(!h||!("error_code"in h)){this.params.error$.next({id:"logRequestUnknown",category:g.NETWORK,message:`Unknown ${s} error`,thrown:h});return}const m=h?.error_code;switch(m){case T.PARAM_SESSION_EXPIRED:case T.PARAM_SESSION_KEY:case T.PARAM_SIGNATURE:return this.authorized$.next(!1),this.sessionKey=await this._authorizeWithBackoff(),u>0?a(r,u-1):void 0;case T.AUTH_LOGIN:return this.authorized$.next(!1),this.authToken=await this.refreshAuthToken(),this.sessionKey=await this._authorizeWithBackoff(),u>0?a(r,u-1):void 0;default:{this.params.error$.next({id:`LogRequest#${m}`,category:g.EXTERNAL_API,message:`${s} error`,data:h});return}}}};return a(t)}destroy(){window.clearTimeout(this.backoffTimeoutId),this.backoffTimeoutId=0}async refreshAuthToken(){if(this.params.refreshAuthToken)return this.refreshAuthTokenPromise||(this.refreshAuthTokenPromise=this.params.refreshAuthToken().finally(()=>{this.refreshAuthTokenPromise=void 0})),this.refreshAuthTokenPromise}createLogParams(t){return{collector:"ok.mobile.apps.video",data:JSON.stringify({application:`@vkontakte/videoplayer-statistics:${q}`,platform:this.params.mobile?"M_WEB":"WEB",items:t})}}_authorizeWithBackoff(){if(!this.consequentAuthErrors)return this._authorize();const t=J(this.consequentAuthErrors,ht);return new Promise(s=>{this.backoffTimeoutId||(this.backoffTimeoutId=window.setTimeout(()=>{this._authorize().then(s).catch(i=>this.params.error$.next({id:"AuthorizeBackoff",category:g.NETWORK,message:"Otherwise unhandled error in authorization",thrown:i})).finally(()=>this.backoffTimeoutId=0)},t))})}async _authorize(){if(this.authorizePromise)return this.authorizePromise;this.sessionKey=void 0,this.authorized$.next(!1);const t="auth.anonymLogin",s={session_data:{version:2,device_id:this.uuid,client_version:q.split("-")[0],client_type:"SDK_JS"}};return this.authToken!==void 0&&(s.session_data.auth_token=this.authToken,s.session_data.version=3),this.authorizePromise=this.params.apiTransport.sendRequest(t,s).then(i=>((!i||!i.session_key)&&this.params.error$.next({id:"AuthorizeFailed",category:g.EXTERNAL_API,message:"No session key",data:i}),this.sessionKey=i?.session_key??void 0,this.sessionKey)).catch(async i=>{this.sessionKey=void 0;const o=i?.error_code;switch(o){case T.AUTH_LOGIN:return this.authToken=await this.refreshAuthToken(),this._authorizeWithBackoff()}o?this.params.error$.next({id:`Authorize#${o}`,category:g.EXTERNAL_API,message:"authorize error",data:i}):this.params.error$.next({id:"AuthorizeUnknown",category:g.NETWORK,message:"authorize error",thrown:i})}).finally(()=>{this.authorizePromise=void 0,this.consequentAuthErrors=this.sessionKey?0:this.consequentAuthErrors+1,this.authorized$.next(this.sessionKey!==void 0)}),this.authorizePromise}}class ct{apiKey;apiBaseUrl;apiEnv;timeSynchronisation;isApiBaseUrlFetched;params;constructor(t){this.params=t,this.apiKey=t.apiKey,this.apiEnv=t.apiEnv,this.apiBaseUrl=t.apiBaseUrl,this.timeSynchronisation=t.timeSynchronisation,this.isApiBaseUrlFetched=!1}async resolveApiBaseUrl(){if(this.apiEnv!==b.AUTO||this.isApiBaseUrlFetched)return this.apiBaseUrl;try{const t=atob("aHR0cHM6Ly9kbnMuZ29vZ2xlL3Jlc29sdmU/bmFtZT12aWRlby5fZW5kcG9pbnQub2sucnUmdHlwZT1UWFQ="),o=(await(await fetch(t,{method:"GET",mode:"cors",cache:"no-cache"})).json())?.Answer[0]?.data;if(!o)throw new Error("Wrong DNS response");return o}catch(t){return this.params.error$.next({id:"OneStat:ApiTransport:resolveApiBaseUrl",category:g.NETWORK,message:"Unhandled resolve api base url error",thrown:t}),j[b.VK_ALIAS]}finally{this.isApiBaseUrlFetched=!0}}sendBeacon(t,s,i){if(!window.Blob||!window.navigator.sendBeacon)return!1;const o=this._prepareQueryParams({method:t,queryParams:s,sessionKey:i}),a=new window.Blob([o.toString()],{type:"application/x-www-form-urlencoded"});try{return window.navigator.sendBeacon(`${this.apiBaseUrl}/fb.do`,a)}catch(r){this.params.error$.next({id:"OneStat:ApiTransport:sendBeacon",category:g.NETWORK,message:"Unhandled beacon error",thrown:r,data:{method:t,params:s}})}return!1}async sendRequest(t,s,i){const o=f(),a=r=>{if(r instanceof TypeError&&["Failed to fetch","NetworkError when attempting to fetch resource."].includes(r.message)){this.params.error$.next({id:"Network",category:g.NETWORK,message:"Request failed",thrown:r});return}this.params.error$.next({id:"OneStat:ApiTransport:sendRequest",category:g.NETWORK,message:"Unhandled request error",thrown:r,data:{method:t,params:s,sessionKey:i,time:f()-o}})};return this.apiBaseUrl=await this.resolveApiBaseUrl(),fetch(`${this.apiBaseUrl}/fb.do`,{method:"post",headers:{"Content-type":"application/x-www-form-urlencoded"},body:this._prepareQueryParams({method:t,queryParams:s,sessionKey:i})}).then(r=>{const u=Number(r.headers.get("content-length"))===0,h=new Date(r.headers.get("date")??"").getTime(),m=f()-o;if(isFinite(h)&&this.timeSynchronisation.addServerTime(h,m),!u)return r.json().then(n=>Object.prototype.hasOwnProperty.call(n,"error_msg")?Promise.reject(n):n,a)},a)}_prepareQueryParams(t){const s=new URLSearchParams({format:"JSON",method:t.method,application_key:this.apiKey});return t.sessionKey!==void 0&&s.append("session_key",t.sessionKey),Object.entries(t.queryParams).forEach(([i,o])=>s.append(i,typeof o=="string"?o:JSON.stringify(o))),s}}const Q=5e3,dt=1e4,lt=6e4,O="onestat_events",ft=36*60*60*1e3,D=e=>e.timestamp+ft>=f();class pt{api;error$;subscription;userSalt;unsaltedStorage=[];lastVsid;isPaused=!1;constructor(t){this.api=t.api,this.error$=t.error$,this.userSalt=t.userSalt,this.subscription=this.api.authorized$.subscribe(s=>{s&&this.debouncedFlush()}),this.housekeepStorage()}isUrgent=t=>{const{operation:s,custom:{param:i}}=t;return["action_stop","close_at_empty_buffer"].includes(s)||s==="watch_coverage_record"&&i==="0-0"};safeFlush(){try{this.flush()}catch(t){this.error$.next({id:"LoggerError",category:g.WTF,message:String(t)??"Unknown logger error",thrown:t})}}firstFlush=K(()=>this.safeFlush(),Q,{maxWait:Q});debouncedFlush=K(()=>this.safeFlush(),dt,{maxWait:lt});readFromStorage(){const t=w.get(O);try{return t?JSON.parse(t):{}}catch{}return{}}addToStorage(t){if(!this.userSalt){this.unsaltedStorage.push(t);return}const s=this.readFromStorage(),o=(s[this.userSalt]??[]).filter(D);w.set(O,JSON.stringify({...s,[this.userSalt]:[...o,t]}))}getFromStorage(){return this.userSalt?(this.readFromStorage()[this.userSalt]??[]).filter(D):this.unsaltedStorage}markStorageSent(){if(!this.userSalt){this.unsaltedStorage=[];return}const t=this.readFromStorage();delete t[this.userSalt],w.set(O,JSON.stringify(t))}housekeepStorage(){const t=this.readFromStorage();for(const[s,i]of Object.entries(t)){const o=i.filter(D);o.length?t[s]=o:delete t[s]}w.set(O,JSON.stringify(t))}log(t){console.log(t.operation,t.custom.param,t),this.addToStorage(t),!this.isPaused&&(this.isUrgent(t)?this.flush():this.lastVsid!==t.custom.vsid?this.firstFlush():this.debouncedFlush(),this.lastVsid=t.custom.vsid)}flush(t=!1){const s=this.getFromStorage();s.length!==0&&(this.api.authorized$.getValue()?(console.log("flush",s.length,"events",s),t?this.api.logBeacon(s):this.api.logRequest(s),this.markStorageSent()):this.api.authorize(),this.firstFlush.cancel(),this.debouncedFlush.cancel())}pause(){this.isPaused=!0,this.debouncedFlush.cancel()}resume(){this.isPaused=!1,this.debouncedFlush()}destroy(){this.subscription.unsubscribe(),this.firstFlush.cancel(),this.debouncedFlush.cancel()}}const gt=()=>{let e,t;try{e=window.self!==window.top}catch(i){i instanceof DOMException&&i.name==="SecurityError"?e=!0:(e=!1,console.error(i))}try{window.location.ancestorOrigins&&(t=window.location.ancestorOrigins[window.location.ancestorOrigins.length-1])}catch(i){console.error(i)}try{!t&&document.referrer&&(t=document.referrer)}catch(i){console.error(i)}const s=t?new URL(t).hostname:void 0;return{isEmbed:e,host:s}},mt=e=>e&&{[E.INVARIANT]:d.UNKNOWN,[E.Q_144P]:d.Q144P,[E.Q_240P]:d.Q240P,[E.Q_360P]:d.Q360P,[E.Q_480P]:d.Q480P,[E.Q_720P]:d.Q720P,[E.Q_1080P]:d.Q1080P,[E.Q_1440P]:d.Q1440P,[E.Q_2160P]:d.Q2160P,[E.Q_4320P]:d.UNKNOWN}[e],G=e=>e&&{[L.HTTP1]:x.HTTP1,[L.HTTP2]:x.HTTP2,[L.QUIC]:x.HTTP3}[e],St=e=>{if(e!==void 0)switch(e){case c.MPEG:return l.MP4;case c.DASH:case c.DASH_LIVE:return l.DASH;case c.DASH_SEP:return l.DASH_SEP;case c.DASH_ONDEMAND:return l.ONDEMAND_DASH;case c.DASH_WEBM:case c.DASH_LIVE_WEBM:return l.WEBM;case c.DASH_LIVE_CMAF:return l.ONDEMAND_DASH_LIVE;case c.HLS:case c.HLS_LIVE:return l.HLS;case c.HLS_ONDEMAND:return l.ONDEMAND_HLS;case c.HLS_LIVE_CMAF:return l.ONDEMAND_HLS_LIVE;case c.WEB_RTC_LIVE:return l.WEBRTC;default:return X(e)}},U=e=>{if(e!==void 0)switch(e){case N.NONE:case N.INLINE:return;case N.FULLSCREEN:return P.FULLSCREEN;case N.SECOND_SCREEN:return P.EXTERNAL;case N.PIP:return P.PIP;default:return X(e)}},Et=e=>{switch(e){case"slow-2g":return y.POOR;case"2g":return y.POOR;case"3g":return y.GOOD;case"4g":return y.EXCELLENT}};class _t{offset=void 0;constructor(t){this.offset=t}getOffset(){return this.offset??0}setOffset(t){this.offset=t}now(){return Date.now()+(this.offset??0)}date(t=new Date){return t.setTime(t.getTime()+(this.offset??0)),t}addServerTime(t,s){const i=Date.now(),o=t-i-s/2;if(Math.abs(o)<1e3){this.offset=0;return}this.offset===void 0?this.offset=Math.round(o):this.offset=Math.round(.2*o+(1-.2)*this.offset)}}const Y=()=>Math.floor(Math.random()*2**32).toString(36).padStart(8,"0"),bt=e=>new Z(t=>{const s=new $,i=k(B(window,"beforeunload"),e.events.willDestruct$),o=new H(void 0);let a;e.info.isLive$.pipe(tt()).subscribe(A=>{a&&(a.unsubscribe(),o.next(void 0)),A?a=e.info.liveTime$.pipe(et(C=>C&&C/1e3)).subscribe(o):a=e.info.position$.subscribe(o),s.add(a)});const{playing$:r,paused$:u}=e.events,h=e.events.willSeek$.pipe(M(()=>e.info.playbackState$.getValue()===V.PLAYING)),m=e.events.seeked$.pipe(M(()=>e.info.playbackState$.getValue()===V.PLAYING)),n=k(r,m),p=k(u,h,i,e.events.looped$);let S;return s.add(n.subscribe(()=>S=o.getValue())),s.add(p.subscribe(()=>{const A=o.getValue();F(S)||S===A||F(A)||(t.next({from:S,to:A}),S=void 0)})),s});class wt{api;logger;config;subscription=new $;timeSynchronisation=new _t;experimental;statContext;vsid=Y();isEmbed;embedParent;uiEvents;player;seekAction$=new H(I.UNKNOWN);constructor(t,s){this.statContext=t,this.config={...s.config??{},requestRetryCount:1};const i=new st;this.experimental={error$:i};const o=new ct({apiKey:rt,apiBaseUrl:j[s.apiEnv],apiEnv:s.apiEnv,error$:i,timeSynchronisation:this.timeSynchronisation});this.api=new ut({apiTransport:o,refreshAuthToken:s.refreshAuthToken,requestRetryCount:this.config.requestRetryCount,mobile:this.statContext.mobile??!1,error$:i}),this.logger=new pt({api:this.api,error$:i,userSalt:s.userSalt});const{isEmbed:a,host:r}=gt();this.isEmbed=a,this.embedParent=r,this.subscribe()}updateContext(t){this.statContext={...this.statContext,...t}}attachTo(t){const s=new $,i=(n,p)=>s.add(n.subscribe(p));i(t.events.willStart$,()=>{this.logActionPlay({position:t.info.position$.getValue()})}),i(t.events.looped$,()=>{this.resetViewSession(),this.logActionPlay({position:t.info.position$.getValue()})}),i(t.events.seeked$,()=>{this.logSeek({action:this.seekAction$.getValue(),time:t.info.position$.getValue()}),this.seekAction$.next(I.UNKNOWN)}),i(t.events.paused$,()=>{this.logPause({position:t.info.position$.getValue()})}),i(t.events.willResume$,()=>{this.logPlay()}),i(t.events.started$,()=>{this.statContext.clip&&this.logWatchCoverageRecord({start:0,end:0})}),i(bt(t),n=>{const p=t.info.isLive$.getValue(),S={start:n.from,end:n.to};p?this.logWatchCoverageLive(S):this.logWatchCoverageRecord(S)});let o;i(t.info.isStalled$,n=>{n?o=f():(v(o)&&this.logEmptyBuffer({duration:f()-o}),o=void 0)});let a=!1;s.add(t.events.fatalError$.pipe(it()).subscribe(()=>a=!0)),i(t.events.willStop$,()=>{if(t.info.isStalled$.getValue()){const p=v(o)?f()-o:void 0;this.logCloseAtEmptyBuffer({duration:p??0}),o=void 0}else a||this.logActionStop()}),i(t.events.managedError$,({id:n})=>{this.logError({fatal:!1,errorType:n})}),i(t.events.fatalError$,({id:n})=>{this.logError({fatal:!0,errorType:n})});let r,u,h=!1;i(t.events.firstBytes$,n=>{r=f(),this.logFirstBytes({time:n})}),i(t.events.willStart$,()=>u=f()),i(t.info.currentBuffer$,n=>{!h&&n&&n.end-n.start>0&&v(r)&&(this.logPlayerReady({duration:f()-r}),h=!0)}),i(t.events.firstFrame$,()=>{v(r)&&!h&&(this.logPlayerReady({duration:f()-r}),h=!0),v(u)&&this.logFirstFrame({time:f()-u})}),i(t.info.atLiveEdge$,n=>this.updateContext({liveEdge:n})),i(W({muted:t.info.muted$,volume:t.info.volume$}),({muted:n,volume:p})=>this.updateContext({audible:!n&&p>0})),i(t.info.currentQuality$,n=>this.updateContext({quality:mt(n)})),i(t.info.isAutoQualityEnabled$,n=>this.updateContext({autoQuality:n})),i(t.info.currentFormat$,n=>this.updateContext({contentType:St(n)}));let m;return i(t.info.hostname$,n=>{this.updateContext({cdnHostname:n,failover:m!==void 0&&m!==n}),m=n}),i(t.info.throughputEstimation$,n=>this.updateContext({downloadSpeed:n})),i(t.info.httpConnectionType$,n=>{this.statContext.firstConnectionType||this.updateContext({firstConnectionType:G(n)}),this.updateContext({connectionType:G(n)})}),i(t.info.httpConnectionReused$,n=>{F(this.statContext.firstConnectionReused)&&this.updateContext({firstConnectionReused:n}),this.updateContext({connectionReused:n})}),i(t.info.surface$,n=>this.updateContext({mode:U(n)})),i(W({current:t.info.currentTextTrack$,available:t.info.availableTextTracks$}),({current:n,available:p})=>{const S=p.find(({id:A})=>A===n);this.updateContext({subtitles:S?.language})}),this.player=t,this.uiEvents&&this.attachToUi(this.uiEvents),this.subscription.add(s),s}attachToUi(t){this.uiEvents=t;const s=new $;return this.player&&(s.add(t.actionRewind$.subscribe(()=>{this.player&&(this.resetViewSession(),this.logActionPlay({position:this.player.info.position$.getValue()}))})),s.add(t.actionSeek$.subscribe(this.seekAction$)),s.add(t.inPiP$.subscribe(i=>{const o=i?P.PIP:U(this.player?.info.surface$.getValue());this.updateContext({mode:o})})),s.add(t.inFullscreen$.subscribe(i=>{const o=i?P.FULLSCREEN:U(this.player?.info.surface$.getValue());this.updateContext({mode:o})})),s.add(t.actionSetSubtitle$.subscribe(i=>{this.updateContext({subtitles:i})})),this.subscription.add(s)),s}attachToAds(t){const s=new $;return s.add(t.slotRequested$.subscribe(()=>this.logAdSlotRequest())).add(t.started$.subscribe(i=>this.logAdStarted(i))).add(t.paused$.subscribe(()=>this.logAdPaused())).add(t.resumed$.subscribe(()=>this.logAdResumed())).add(t.ended$.subscribe(()=>this.logAdEnded())).add(t.skipped$.subscribe(()=>this.logAdSkipped())).add(t.clicked$.subscribe(()=>this.logAdClicked())),this.subscription.add(s),s}authorize(t){return this.api.authorize(t)}pause(){this.logger.pause()}resume(){this.logger.resume()}destroy(){this.logger.flush(),this.subscription.unsubscribe(),this.api.destroy(),this.logger.destroy()}resetViewSession(){this.vsid=Y()}setFieldBlacklist(){}logInited(){}logReady(t){this.log({operation:"player_ready",param:String(t.time)})}logStarted(...t){this.logActionPlay(...t)}logPlay(){this.log({operation:"play_toggle"})}logPause(t){this.log({operation:"pause",param:String(Math.round(t.position))})}logSeek(t){this.log({operation:"seek",param:t.action,time:t.time})}logFirstBytes(t){this.log({operation:"first_bytes",param:String(t.time)})}logFirstFrame(t){this.log({operation:"first_frame",param:String(t.time)})}logError(t){this.log({operation:"content_error",param:`${t.fatal?"fatal":"recoverable"}_${t.errorType}`})}logWatchCoverageRecord(t){this.log({operation:"watch_coverage_record",param:`${t.start}-${t.end}`})}logWatchCoverageLive(t){const s=t.start+this.timeSynchronisation.getOffset(),i=t.end+this.timeSynchronisation.getOffset();this.log({operation:"watch_coverage_live",param:`${s}-${i}`})}logEmptyBuffer(t){this.log({operation:"empty_buffer",param:String(t.duration)})}logDownloadSpeed(){}logAdSlotRequest(){this.log({operation:"adv",param:"slot_request"})}logAdStarted(t){this.log({operation:"adv",param:t})}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"})}subscribe(){this.subscription.add(B(window,"beforeunload").subscribe(()=>this.logger.flush(!0)));const t=window.navigator.connection;t&&"onchange"in t&&"effectiveType"in t&&this.subscription.add(k(B(t,"change"),ot(["init"])).subscribe(()=>this.updateContext({network:Et(t.effectiveType)})))}logPlayerReady(t){this.log({operation:"player_ready",param:String(Math.round(t.duration))})}logActionPlay(t){this.log({operation:"action_play",param:String(Math.round(t.position))})}logCloseAtEmptyBuffer(t){this.log({operation:"close_at_empty_buffer",param:String(Math.round(t.duration))})}logActionStop(){this.log({operation:"action_stop"})}log(t){const s=this.createLogItem(t);this.logger.log(s)}createLogItem({operation:t,param:s,time:i}){const o=this.timeSynchronisation.now(),a=t==="empty_buffer"||t==="close_at_empty_buffer"?this.statContext.connectionType:this.statContext.firstConnectionType,r=this.statContext.firstConnectionReused,u={vsid:this.vsid,vid:this.statContext.movieId,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?R.AUTO:this.statContext.autoplay===!1?R.MANUAL:void 0,param:s,vk_app_id:this.statContext.vkAppId,track_code:this.statContext.trackCode,connection_type:a,connection_reused:r===!0?_.YES:r===!1?_.NO:void 0,cached_data:this.statContext.cached===!0?_.YES:this.statContext.cached===!1?_.NO:void 0,live:this.statContext.liveEdge?_.YES:void 0,muted:this.statContext.audible===!1?_.YES:void 0,mode:this.statContext.mode,subtitles:this.statContext.subtitles,download_speed:this.statContext.downloadSpeed,manual_quality:this.statContext.autoQuality?void 0:_.YES,ref_domain:this.embedParent,aid:this.statContext.albumId,vk_playlist_id:this.statContext.vkPlaylistId};return{operation:t,type:1,time:i,network:this.statContext.network,timestamp:o,custom:u}}}export{b as ApiEnv,x as ConnectionType,l as ContentType,wt as OneStat,d as Quality,I as SeekAction,q as VERSION};
package/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { ISubscription, IObservable, IError, Seconds, Milliseconds } from '@vkontakte/videoplayer-shared';
2
1
  import { IPlayer } from '@vkontakte/videoplayer-core';
2
+ import { Kbps, IObservable, ISubscription, IError, Seconds, Milliseconds, Subscription } from '@vkontakte/videoplayer-shared';
3
3
 
4
4
  declare enum ApiEnv {
5
5
  PROD = "prod",
@@ -9,24 +9,16 @@ declare enum ApiEnv {
9
9
  AUTO = "auto"
10
10
  }
11
11
  declare enum Quality {
12
- Q144P = "144",
13
- Q240P = "240",
14
- Q360P = "360",
15
- Q480P = "480",
16
- Q720P = "720",
17
- Q1080P = "1080",
18
- Q1440P = "1440",
19
- Q2160P = "2160",
20
- Q4320P = "4320",
12
+ Q144P = "mobile",
13
+ Q240P = "lowest",
14
+ Q360P = "low",
15
+ Q480P = "medium",
16
+ Q720P = "high",
17
+ Q1080P = "fullhd",
18
+ Q1440P = "quadhd",
19
+ Q2160P = "ultrahd",
21
20
  UNKNOWN = "unknown"
22
21
  }
23
- declare enum ScreenDestination {
24
- CHROMECAST = "chromecast",
25
- APPLE_TV = "appletv",
26
- PIP = "pip",
27
- HEADPHONES = "externalHeadphones",
28
- SCREEN = ""
29
- }
30
22
  declare enum ContentType {
31
23
  MP4 = "mp4",
32
24
  DASH = "dash",
@@ -35,8 +27,12 @@ declare enum ContentType {
35
27
  HLS = "hls",
36
28
  ONDEMAND_HLS = "ondemand_hls",
37
29
  WEBM = "webm",
38
- RTMP = "rtmp",
39
- UNKNOWN = "unknown"
30
+ ONDEMAND_DASH_LIVE = "ondemand_dash_live",
31
+ ONDEMAND_HLS_LIVE = "ondemand_hls_live",
32
+ WEBRTC = "webrtc",
33
+ UNKNOWN = "unknown",
34
+ /** @deprecated сдк не поддерживает RTMP, не нужно его использовать */
35
+ RTMP = "rtmp"
40
36
  }
41
37
  declare enum StatType {
42
38
  AUTO = "auto",
@@ -47,37 +43,76 @@ declare enum ConnectionType {
47
43
  HTTP2 = "http2",
48
44
  HTTP3 = "http3"
49
45
  }
50
- declare enum ConnectionReused {
46
+ declare enum Flag {
51
47
  YES = 1,
52
48
  NO = 0
49
+ }
50
+ declare enum Network {
51
+ EXCELLENT = "excellent",
52
+ GOOD = "good",
53
+ POOR = "poor"
54
+ }
55
+ declare enum Mode {
56
+ PIP = "pip",
57
+ FULLSCREEN = "fullscreen",
58
+ EXTERNAL = "external",
59
+ PREFETCH = "prefetch"
60
+ }
61
+ declare enum SeekAction {
62
+ SLIDER = "slider",
63
+ DOUBLE_TAP = "double_tap",
64
+ TIME_CODE = "time_code",
65
+ EPISODE = "episode",
66
+ REWIND = "rewind",
67
+ LIVE = "live",
68
+ UNKNOWN = "unknown"
53
69
  }
54
70
 
55
71
  interface ILogCustomData {
56
- /**
57
- * Id видео
58
- */
59
- vid: number;
60
72
  /**
61
73
  * Сессия смотрения, уникальна для пользователя, контента, экземпляра плеера и одного просмотра
62
74
  * См https://confluence.vk.team/display/VID/Video%20Session%20ID
63
75
  */
64
- vsid?: string;
65
- quality?: Quality;
66
- dst?: ScreenDestination;
76
+ vsid: string;
77
+ vid: number;
67
78
  ct?: ContentType;
68
- aid?: string;
69
79
  place?: string;
80
+ quality?: Quality;
70
81
  cdn_host?: string;
71
- connection_type?: ConnectionType;
72
- connection_reused?: ConnectionReused;
73
82
  stat_type?: StatType;
74
- vk_playlist_id?: string;
83
+ param?: any;
75
84
  vk_app_id?: string;
76
85
  track_code?: string;
77
- /**
78
- * Параметр события (имеет разный смысл в каждом из событий)
79
- */
80
- param?: any;
86
+ connection_type?: ConnectionType;
87
+ connection_reused?: Flag;
88
+ cached_data?: Flag;
89
+ live?: Flag;
90
+ muted?: Flag;
91
+ mode?: Mode;
92
+ subtitles?: string;
93
+ failover?: Flag;
94
+ download_speed?: Kbps;
95
+ manual_quality?: Flag;
96
+ ref_domain?: string;
97
+ vk_playlist_id?: string;
98
+ aid?: string;
99
+ }
100
+
101
+ interface IUIEvents {
102
+ actionRewind$: IObservable<void>;
103
+ actionSeek$: IObservable<SeekAction>;
104
+ inPiP$: IObservable<boolean>;
105
+ inFullscreen$: IObservable<boolean>;
106
+ actionSetSubtitle$: IObservable<string>;
107
+ }
108
+ interface IAdsEvents {
109
+ slotRequested$: IObservable<void>;
110
+ started$: IObservable<string>;
111
+ paused$: IObservable<void>;
112
+ resumed$: IObservable<void>;
113
+ ended$: IObservable<void>;
114
+ skipped$: IObservable<void>;
115
+ clicked$: IObservable<void>;
81
116
  }
82
117
 
83
118
  interface IOneStat {
@@ -93,6 +128,8 @@ interface IOneStat {
93
128
  */
94
129
  updateContext(context: Partial<IStatContext>): void;
95
130
  attachTo(player: IPlayer): ISubscription;
131
+ attachToUi(uiEvents: IUIEvents): ISubscription;
132
+ attachToAds(adsEvents: IAdsEvents): ISubscription;
96
133
  /**
97
134
  * Ставит отправку событий на паузу. Они будут накапливаться до вызова `resume()`
98
135
  */
@@ -110,70 +147,107 @@ interface IOneStat {
110
147
  */
111
148
  resetViewSession(): void;
112
149
  /**
150
+ * @deprecated
113
151
  * Отключает отправку полей событий
114
152
  */
115
153
  setFieldBlacklist(fields: (keyof ILogCustomData)[]): void;
116
154
  /**
117
- * Плеер создался и получил ссылку
155
+ * @deprecated
118
156
  */
119
157
  logInited(): void;
120
158
  /**
159
+ * @deprecated публично нужно использовать attachTo
121
160
  * Плеер готов начать воспроизведение
122
161
  */
123
162
  logReady(payload: IReadyPayload): void;
124
163
  /**
164
+ * @deprecated публично нужно использовать attachTo
125
165
  * Первый старт видео. Отправляется 1 раз, когда фактически началось воспроизведение ролика, а также после зацикливания, но не после паузы.
126
166
  *
127
167
  * В документации API может называться `play`
128
168
  */
129
169
  logStarted(payload: IActionPlayPayload): void;
130
170
  /**
171
+ * @deprecated публично нужно использовать attachTo
131
172
  * Возобновление после паузы
132
173
  *
133
174
  * В документации API может называться `play_toggle`
134
175
  */
135
176
  logPlay(): void;
136
177
  /**
178
+ * @deprecated публично нужно использовать attachTo
137
179
  * Постановка видео на паузу
138
180
  */
139
181
  logPause(payload: IPausePayload): void;
140
182
  /**
183
+ * @deprecated публично нужно использовать attachTo
141
184
  * перемотка видео
142
185
  */
143
- logSeek(): void;
186
+ logSeek(payload: ISeekPayload): void;
144
187
  /**
188
+ * @deprecated публично нужно использовать attachTo
145
189
  * Промежутки видео, которые были просмотрены пользователем.
146
190
  * Время указывается в секундах от начала ролика (т.е. является позицией).
147
191
  */
148
192
  logWatchCoverageRecord(payload: IWatchCoveragePayload): void;
149
193
  /**
194
+ * @deprecated публично нужно использовать attachTo
150
195
  * Промежутки видео, которые были просмотрены пользователем.
151
196
  * Время указывается в виде UTC timestamp'ов.
152
197
  */
153
198
  logWatchCoverageLive(payload: IWatchCoveragePayload): void;
154
199
  /**
200
+ * @deprecated публично нужно использовать attachTo
155
201
  * Буфер пуст и пользователь видит залипший кадр.
156
202
  */
157
203
  logEmptyBuffer(payload: IEmptyBufferPayload): void;
158
204
  /**
205
+ * @deprecated публично нужно использовать attachTo
159
206
  * Получен первый кусок данных контента от сервера раздачи видео
160
207
  */
161
208
  logFirstBytes(payload: IFirstBytesPayload): void;
162
209
  /**
210
+ * @deprecated публично нужно использовать attachTo
163
211
  * Пользователь увидел первый кадр
164
212
  */
165
213
  logFirstFrame(payload: IFirstFramePayload): void;
214
+ /**
215
+ * @deprecated публично нужно использовать attachTo
216
+ * @param payload
217
+ */
166
218
  logDownloadSpeed(payload: IDownloadSpeedPayload): void;
167
219
  /**
168
220
  * произошла ошибка
169
221
  */
170
222
  logError(payload: IErrorPayload): void;
223
+ /**
224
+ * @deprecated публично нужно использовать attachToAds
225
+ */
171
226
  logAdSlotRequest(): void;
227
+ /**
228
+ * @deprecated публично нужно использовать attachToAds
229
+ * @param sectionType
230
+ */
172
231
  logAdStarted(sectionType: string): void;
232
+ /**
233
+ * @deprecated публично нужно использовать attachToAds
234
+ */
173
235
  logAdPaused(): void;
236
+ /**
237
+ * @deprecated публично нужно использовать attachToAds
238
+ */
174
239
  logAdResumed(): void;
240
+ /**
241
+ * @deprecated публично нужно использовать attachToAds
242
+ */
175
243
  logAdEnded(): void;
244
+ /**
245
+ * @deprecated публично нужно использовать attachToAds
246
+ */
176
247
  logAdSkipped(): void;
248
+ /**
249
+ * @deprecated публично нужно использовать attachToAds
250
+ */
177
251
  logAdClicked(): void;
178
252
  experimental: {
179
253
  error$: IObservable<IError>;
@@ -191,6 +265,13 @@ interface IPausePayload {
191
265
  */
192
266
  position: Seconds;
193
267
  }
268
+ interface ISeekPayload {
269
+ /**
270
+ * Действие пользователя приведшее к перемотке
271
+ */
272
+ action: SeekAction;
273
+ time: Seconds;
274
+ }
194
275
  interface IReadyPayload {
195
276
  /**
196
277
  * время в миллисекундах с события inited
@@ -226,27 +307,34 @@ interface IErrorPayload {
226
307
  * строка из символов(без пробелов): тип ошибки
227
308
  */
228
309
  errorType: string;
310
+ fatal: boolean;
229
311
  }
230
312
  interface IStatContext {
231
- /**
232
- * Ид видео
233
- */
313
+ mobile: boolean;
234
314
  movieId: number;
315
+ vkAppId?: string;
316
+ clip?: boolean;
317
+ liveEdge?: boolean;
318
+ autoplay: boolean;
319
+ audible?: boolean;
235
320
  quality?: Quality;
236
- destination?: ScreenDestination;
321
+ autoQuality?: boolean;
237
322
  contentType?: ContentType;
238
323
  albumId?: string;
239
324
  vkPlaylistId?: string;
240
- vkAppId?: string;
241
- /**
242
- * Раздел сайта на котором играет видео: Лента, плеер, личные сообщения, ...
243
- */
325
+ trackCode?: string;
244
326
  place?: string;
245
327
  cdnHostname?: string;
328
+ downloadSpeed?: Kbps;
246
329
  connectionType?: ConnectionType;
247
330
  connectionReused?: boolean;
248
- autoplay?: boolean;
249
- trackCode?: string;
331
+ firstConnectionType?: ConnectionType;
332
+ firstConnectionReused?: boolean;
333
+ network?: Network;
334
+ cached?: boolean;
335
+ mode?: Mode;
336
+ subtitles?: string;
337
+ failover?: boolean;
250
338
  }
251
339
  interface IConfig {
252
340
  requestRetryCount: number;
@@ -254,6 +342,7 @@ interface IConfig {
254
342
  interface IConstructorParams {
255
343
  apiEnv: ApiEnv;
256
344
  config?: Partial<IConfig>;
345
+ userSalt?: string;
257
346
  refreshAuthToken?: () => Promise<string>;
258
347
  }
259
348
  interface IWatchCoveragePayload {
@@ -271,29 +360,35 @@ declare class OneStat implements IOneStat {
271
360
  };
272
361
  private statContext;
273
362
  private vsid;
274
- private fieldBlacklist;
363
+ private isEmbed;
364
+ private embedParent;
365
+ private uiEvents;
366
+ private player;
367
+ private seekAction$;
275
368
  constructor(statContext: IStatContext, params: IConstructorParams);
276
369
  updateContext(newContext: Partial<IStatContext>): void;
277
370
  attachTo(player: IPlayer): ISubscription;
371
+ attachToUi(events: IUIEvents): Subscription;
372
+ attachToAds(adsEvents: IAdsEvents): ISubscription;
278
373
  authorize(authToken?: string): Promise<unknown>;
279
374
  pause(): void;
280
375
  resume(): void;
281
376
  destroy(): void;
282
377
  resetViewSession(): void;
283
- setFieldBlacklist(fields?: (keyof ILogCustomData)[]): void;
378
+ setFieldBlacklist(): void;
284
379
  logInited(): void;
285
380
  logReady(payload: IReadyPayload): void;
286
- logStarted(payload: IActionPlayPayload): void;
381
+ logStarted(...args: Parameters<typeof this$1.logActionPlay>): void;
287
382
  logPlay(): void;
288
383
  logPause(payload: IPausePayload): void;
289
- logSeek(): void;
384
+ logSeek(payload: ISeekPayload): void;
290
385
  logFirstBytes(payload: IFirstBytesPayload): void;
291
386
  logFirstFrame(payload: IFirstFramePayload): void;
292
387
  logError(payload: IErrorPayload): void;
293
388
  logWatchCoverageRecord(payload: IWatchCoveragePayload): void;
294
389
  logWatchCoverageLive(payload: IWatchCoveragePayload): void;
295
390
  logEmptyBuffer(payload: IEmptyBufferPayload): void;
296
- logDownloadSpeed(payload: IDownloadSpeedPayload): void;
391
+ logDownloadSpeed(): void;
297
392
  logAdSlotRequest(): void;
298
393
  logAdStarted(sectionType: string): void;
299
394
  logAdPaused(): void;
@@ -301,8 +396,15 @@ declare class OneStat implements IOneStat {
301
396
  logAdEnded(): void;
302
397
  logAdSkipped(): void;
303
398
  logAdClicked(): void;
399
+ private subscribe;
400
+ private logPlayerReady;
401
+ private logActionPlay;
402
+ private logCloseAtEmptyBuffer;
403
+ private logActionStop;
304
404
  private log;
305
405
  private createLogItem;
306
406
  }
307
407
 
308
- export { ApiEnv, ConnectionType, ContentType, IOneStat, IStatContext, OneStat, Quality, ScreenDestination };
408
+ declare const VERSION: string;
409
+
410
+ export { ApiEnv, ConnectionType, ContentType, IAdsEvents, IOneStat, IStatContext, IUIEvents, OneStat, Quality, SeekAction, VERSION };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vkontakte/videoplayer-statistics",
3
- "version": "1.0.34",
3
+ "version": "1.0.35-beta.0",
4
4
  "author": "vk.com",
5
5
  "description": "Statistics library for vk.com videoplayer",
6
6
  "homepage": "https://vk.com",
@@ -49,10 +49,10 @@
49
49
  "**/*.d.ts"
50
50
  ],
51
51
  "dependencies": {
52
- "@vkontakte/videoplayer-shared": "^1.0.23",
52
+ "@vkontakte/videoplayer-shared": "1.0.24-beta.3",
53
53
  "lodash": "4.17.21"
54
54
  },
55
55
  "peerDependencies": {
56
- "@vkontakte/videoplayer-core": "^2.0.82"
56
+ "@vkontakte/videoplayer-core": "2.0.87-beta.3"
57
57
  }
58
58
  }