@videts/vide 0.7.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.
@@ -0,0 +1,29 @@
1
+ import { e as Plugin } from '../types-BxRa9Jvl.js';
2
+ import { V as VastResponse, R as ResolveOptions, a as VastPluginOptions } from '../types-BryM58LE.js';
3
+ export { A as AdCategory, b as AdPlugin, c as AdVerification, d as VastProgressEvent } from '../types-BryM58LE.js';
4
+
5
+ /**
6
+ * Parse a VAST XML string into a VastResponse object.
7
+ * Pure function — no side effects, no network access.
8
+ */
9
+ declare function parseVast(xml: string): VastResponse;
10
+ /** Fetch a VAST tag URL. Separated from parsing for testability. */
11
+ declare function fetchVast(tagUrl: string, options?: {
12
+ timeout?: number | undefined;
13
+ }): Promise<string>;
14
+ /**
15
+ * Resolve a VAST tag URL, following Wrapper redirects until an InLine ad is found.
16
+ * Merges tracking from all Wrappers in the chain into the final InLine ad.
17
+ */
18
+ declare function resolveVast(tagUrl: string, options?: ResolveOptions | undefined): Promise<VastResponse>;
19
+
20
+ /** Fire-and-forget tracking. Response is ignored. */
21
+ declare function track(urls: string[]): void;
22
+ type QuartileEvent = "start" | "firstQuartile" | "midpoint" | "thirdQuartile" | "complete";
23
+ /** Determine which quartile the current playback position represents. */
24
+ declare function getQuartile(currentTime: number, duration: number): QuartileEvent | null;
25
+
26
+ /** Create a VAST ad plugin for vide. */
27
+ declare function vast(options: VastPluginOptions): Plugin;
28
+
29
+ export { ResolveOptions, VastPluginOptions, fetchVast, getQuartile, parseVast, resolveVast, track, vast };
@@ -0,0 +1 @@
1
+ import {b,a as a$1}from'../chunk-726XNUGZ.mjs';export{b as fetchVast,a as parseVast,c as resolveVast}from'../chunk-726XNUGZ.mjs';import {a,c}from'../chunk-G4Q7R3SH.mjs';export{b as getQuartile,a as track}from'../chunk-G4Q7R3SH.mjs';function ue(d){return {name:"vast",setup(e){let c$1=false,u=null,s=e._setState;async function q(){if(!c$1){s("ad:loading");try{let N=function(n){if(t)for(let i of t.trackingEvents.progress)!I.has(i.offset)&&n>=i.offset&&(I.add(i.offset),a([i.url]));},v=function(){for(let n of L)n();L.length=0;},g=function(){e.emit("ad:end",{adId:a$2}),s("playing"),U();},U=function(){function n({to:i}){i==="ready"&&(e.off("statechange",n),R>0&&(e.el.currentTime=R),e.play().catch(()=>{e.el.muted=!0,e.play().catch(()=>{});}));}e.on("statechange",n),e.src=D;},V=function(){!t||o||(a(t.clickTracking),e.emit("ad:click",{clickThrough:t.clickThrough,clickTracking:t.clickTracking}));},b$1=function(){!t||o||(a(t.trackingEvents.skip),o=!0,m(),v(),g());},S=function(){!t||o||e.state==="ad:playing"&&(a(t.trackingEvents.pause),s("ad:paused"));},w=function(){!t||o||e.state==="ad:paused"&&(a(t.trackingEvents.resume),s("ad:playing"));},A=function(){_(e.el.currentTime),N(e.el.currentTime);},F=function(){if(!t||o)return;let n=e.el.muted||e.el.volume===0;n&&!P?(a(t.trackingEvents.mute),e.emit("ad:mute",{adId:a$2})):!n&&P&&(a(t.trackingEvents.unmute),e.emit("ad:unmute",{adId:a$2})),P=n,e.emit("ad:volumeChange",{adId:a$2,volume:e.el.muted?0:e.el.volume});},x=function(){if(!t||o)return;let n=!!document.fullscreenElement;n&&!T?(a(t.trackingEvents.playerExpand),e.emit("ad:fullscreen",{adId:a$2,fullscreen:!0})):!n&&T&&(a(t.trackingEvents.playerCollapse),e.emit("ad:fullscreen",{adId:a$2,fullscreen:!1})),T=n;},C=function(){o||(o=!0,m(),v(),e.emit("ad:error",{error:new Error("Ad media playback failed")}),g());},O=function(){o||(o=!0,t&&_(t.duration),m(),v(),g());},E=function(){e.el.removeEventListener("canplay",E),t&&(a(t.trackingEvents.loaded),a(t.trackingEvents.creativeView),e.emit("ad:loaded",{adId:a$2}),s("ad:playing"),e.el.play().catch(()=>{e.el.muted=!0,e.el.play().catch(()=>{});}));},m=function(){e.el.removeEventListener("timeupdate",A),e.el.removeEventListener("ended",O),e.el.removeEventListener("error",C),e.el.removeEventListener("canplay",E),e.el.removeEventListener("pause",S),e.el.removeEventListener("play",w),e.el.removeEventListener("click",V),e.el.removeEventListener("volumechange",F),document.removeEventListener("fullscreenchange",x),e.off("ad:skip",b$1);};var M=N,K=v,W=g,X=U,Y=V,Z=b$1,$=S,y=w,ee=A,te=F,ne=x,ie=C,re=O,se=E,oe=m;let f=d.timeout!==void 0?{timeout:d.timeout}:void 0,B=await b(d.tagUrl,f);if(c$1)return;let h=a$1(B);if(h.ads.length===0){s("playing");return}let t=null,l=null;for(let n of h.ads){for(let i of n.creatives)if(i.linear&&i.linear.mediaFiles.length>0){t=i.linear,l=n;break}if(t)break}if(!t||!l){s("playing");return}let a$2=l.id;e.emit("ad:start",{adId:a$2});let L=[];if(d.adPlugins)for(let n of d.adPlugins(l)){let i=n.setup(e,l);i&&L.push(i);}for(let n of h.ads)a(n.impressions);e.emit("ad:impression",{adId:a$2});let Q=J(t.mediaFiles);if(!Q){e.emit("ad:error",{error:new Error("No suitable media file found")}),s("playing");return}let R=e.el.currentTime,D=e.src,_=c(t.duration,n=>{if(!t)return;let i=t.trackingEvents[n];i&&a(i),e.emit("ad:quartile",{adId:a$2,quartile:n});}),I=new Set,P=e.el.muted||e.el.volume===0,T=!!document.fullscreenElement,o=!1;e.el.addEventListener("canplay",E),e.el.addEventListener("timeupdate",A),e.el.addEventListener("ended",O),e.el.addEventListener("error",C),e.el.addEventListener("pause",S),e.el.addEventListener("play",w),e.el.addEventListener("click",V),e.el.addEventListener("volumechange",F),document.addEventListener("fullscreenchange",x),e.on("ad:skip",b$1),u=m,e.src="",e.el.src=Q.url,e.el.load();}catch(f){if(c$1)return;e.emit("ad:error",{error:f instanceof Error?f:new Error(String(f))}),s("playing");}}}function k({to:M}){M==="ready"&&!c$1&&(e.off("statechange",k),q());}return e.state==="ready"||e.state==="playing"||e.state==="paused"?q():e.on("statechange",k),()=>{c$1=true,e.off("statechange",k),u&&u();}}}}function J(d){if(d.length===0)return null;let e=d.filter(u=>u.mimeType==="video/mp4");return (e.length>0?e:d).sort((u,s)=>(s.bitrate??0)-(u.bitrate??0))[0]}export{ue as vast};
@@ -0,0 +1,77 @@
1
+ import { P as Player, e as Plugin } from '../types-BxRa9Jvl.js';
2
+ import { e as VastAd, b as AdPlugin } from '../types-BryM58LE.js';
3
+
4
+ interface VmapResponse {
5
+ version: string;
6
+ adBreaks: AdBreak[];
7
+ }
8
+ interface AdBreakTrackingEvents {
9
+ breakStart: string[];
10
+ breakEnd: string[];
11
+ error: string[];
12
+ }
13
+ interface AdBreak {
14
+ timeOffset: AdBreakTimeOffset;
15
+ breakType: "linear" | "nonlinear" | "display";
16
+ breakId?: string | undefined;
17
+ adSource: AdSource | null;
18
+ trackingEvents: AdBreakTrackingEvents;
19
+ }
20
+ type AdBreakTimeOffset = {
21
+ type: "start";
22
+ } | {
23
+ type: "end";
24
+ } | {
25
+ type: "time";
26
+ seconds: number;
27
+ } | {
28
+ type: "percentage";
29
+ pct: number;
30
+ };
31
+ interface AdSource {
32
+ id?: string | undefined;
33
+ allowMultipleAds?: boolean | undefined;
34
+ followRedirects?: boolean | undefined;
35
+ vastUrl?: string | undefined;
36
+ vastData?: string | undefined;
37
+ }
38
+
39
+ interface VmapPluginOptions {
40
+ url: string;
41
+ timeout?: number | undefined;
42
+ vastOptions?: {
43
+ timeout?: number | undefined;
44
+ maxDepth?: number | undefined;
45
+ };
46
+ /** Create per-ad plugins. Called once per ad with the parsed VastAd. */
47
+ adPlugins?: ((ad: VastAd) => AdPlugin[]) | undefined;
48
+ }
49
+
50
+ /**
51
+ * Parse a VMAP XML string into a VmapResponse object.
52
+ * Pure function — no side effects, no network access.
53
+ */
54
+ declare function parseVmap(xml: string): VmapResponse;
55
+
56
+ interface Scheduler {
57
+ start(): void;
58
+ pause(): void;
59
+ resume(): void;
60
+ destroy(): void;
61
+ }
62
+ /**
63
+ * Creates a scheduler that monitors playback and fires onBreak
64
+ * when the player reaches an ad break's time offset.
65
+ *
66
+ * - preroll ("start"): fires immediately on start()
67
+ * - midroll ("time"): fires when currentTime >= seconds (±0.5s tolerance)
68
+ * - postroll ("end"): fires on "ended" event
69
+ * - Each break fires at most once
70
+ * - Midrolls skipped via seek do not fire
71
+ */
72
+ declare function createScheduler(player: Player, adBreaks: AdBreak[], onBreak: (adBreak: AdBreak) => Promise<void>): Scheduler;
73
+
74
+ /** Create a VMAP ad plugin for vide. */
75
+ declare function vmap(options: VmapPluginOptions): Plugin;
76
+
77
+ export { type AdBreak, type AdBreakTimeOffset, type AdBreakTrackingEvents, type AdSource, type VmapPluginOptions, type VmapResponse, createScheduler, parseVmap, vmap };
@@ -0,0 +1 @@
1
+ import {a as a$1,c}from'../chunk-726XNUGZ.mjs';import {a,c as c$1}from'../chunk-G4Q7R3SH.mjs';function O(t){let i=new DOMParser().parseFromString(t,"text/xml");if(i.querySelector("parsererror"))return {version:"",adBreaks:[]};let o=i.documentElement;if((o.localName||o.tagName)!=="VMAP")return {version:"",adBreaks:[]};let c=o.getAttribute("version")??"",g=[];for(let d=0;d<o.children.length;d++){let n=o.children[d];if((n.localName||n.tagName)==="AdBreak"){let k=_(n);k&&g.push(k);}}return {version:c,adBreaks:g}}function _(t){let e=t.getAttribute("timeOffset");if(!e)return null;let i=H(e),r=t.getAttribute("breakType"),o=r==="nonlinear"?"nonlinear":r==="display"?"display":"linear",f=t.getAttribute("breakId")??void 0,c=null,g={breakStart:[],breakEnd:[],error:[]};for(let d=0;d<t.children.length;d++){let n=t.children[d],v=n.localName||n.tagName;v==="AdSource"?c=X(n):v==="TrackingEvents"&&z(n,g);}return {timeOffset:i,breakType:o,breakId:f,adSource:c,trackingEvents:g}}function z(t,e){for(let i=0;i<t.children.length;i++){let r=t.children[i];if((r.localName||r.tagName)!=="Tracking")continue;let f=r.getAttribute("event"),c=(r.textContent??"").trim();!f||!c||(f==="breakStart"?e.breakStart.push(c):f==="breakEnd"?e.breakEnd.push(c):f==="error"&&e.error.push(c));}}function X(t){let e=t.getAttribute("id")??void 0,i=t.getAttribute("allowMultipleAds"),r=i!=null?i==="true":void 0,o=t.getAttribute("followRedirects"),f=o!=null?o==="true":void 0,c,g;for(let d=0;d<t.children.length;d++){let n=t.children[d],v=n.localName||n.tagName;if(v==="VASTAdData"){let k=n.querySelector("VAST");k&&(c=new XMLSerializer().serializeToString(k));}else v==="AdTagURI"&&(g=(n.textContent??"").trim());}return {id:e,allowMultipleAds:r,followRedirects:f,vastUrl:g,vastData:c}}function H(t){if(t==="start")return {type:"start"};if(t==="end")return {type:"end"};if(t.endsWith("%")){let e=Number.parseFloat(t);return Number.isNaN(e)?{type:"time",seconds:0}:{type:"percentage",pct:e}}return {type:"time",seconds:K(t)}}function K(t){if(!t)return 0;let e=t.split(":");if(e.length!==3)return 0;let i=Number.parseInt(e[0],10),r=Number.parseInt(e[1],10),o=Number.parseFloat(e[2]);return Number.isNaN(i)||Number.isNaN(r)||Number.isNaN(o)?0:i*3600+r*60+o}function V(t,e,i){let r=new Set,o=0,f=false,c=false,g=e.filter(a=>a.timeOffset.type==="start"),d=e.filter(a=>a.timeOffset.type==="end"),n=e.filter(a=>a.timeOffset.type==="time"||a.timeOffset.type==="percentage").sort((a,b)=>U(a)-U(b));function v(){if(c)return;let a=t.currentTime,b=a>o+1.5;for(let E of n){if(r.has(E))continue;let T=Q(E,t.duration);if(T!==null){if(b&&a>T+.5){r.add(E);continue}a>=T-.5&&(r.add(E),i(E));}}o=a;}function k(){if(!c)for(let a of d)r.has(a)||(r.add(a),i(a));}function p(){if(!f){f=true;for(let a of g)r.has(a)||(r.add(a),i(a));t.on("timeupdate",v),t.on("ended",k);}}function s(){c=true;}function A(){c=false,o=t.currentTime;}function u(){t.off("timeupdate",v),t.off("ended",k);}return {start:p,pause:s,resume:A,destroy:u}}function Q(t,e){return t.timeOffset.type==="time"?t.timeOffset.seconds:t.timeOffset.type==="percentage"?t.timeOffset.pct/100*e:null}function U(t){return t.timeOffset.type==="time"?t.timeOffset.seconds:t.timeOffset.type==="percentage"?t.timeOffset.pct:0}function te(t){return {name:"vmap",setup(e){let i=false,r=null,o=null,f=e._setState;async function c$2(n){if(!(i||!n.adSource)){r&&r.pause(),a(n.trackingEvents.breakStart),e.emit("ad:breakStart",{breakId:n.breakId});try{let b=function(){for(let m of a$2)m();a$2.length=0;},x=function(m){if(s)for(let l of s.trackingEvents.progress)!y.has(l.offset)&&m>=l.offset&&(y.add(l.offset),a([l.url]));};var v=b,k=x;let p;if(n.adSource.vastData)p=a$1(n.adSource.vastData);else if(n.adSource.vastUrl)p=await c(n.adSource.vastUrl,t.vastOptions);else return;if(i||p.ads.length===0)return;let s=null,A=null;for(let m of p.ads){for(let l of m.creatives)if(l.linear&&l.linear.mediaFiles.length>0){s=l.linear,A=m;break}if(s)break}if(!s||!A)return;let u=A.id;f("ad:loading"),e.emit("ad:start",{adId:u});let a$2=[];if(t.adPlugins)for(let m of t.adPlugins(A)){let l=m.setup(e,A);l&&a$2.push(l);}for(let m of p.ads)a(m.impressions);e.emit("ad:impression",{adId:u});let E=W(s.mediaFiles);if(!E){e.emit("ad:error",{error:new Error("No suitable media file found")}),b(),f("playing");return}let T=e.el.currentTime,D=e.el.paused,q=e.el.src,B=c$1(s.duration,m=>{if(!s)return;let l=s.trackingEvents[m];l&&a(l),e.emit("ad:quartile",{adId:u,quartile:m});}),y=new Set,N=e.el.muted||e.el.volume===0,w=!!document.fullscreenElement;await new Promise(m=>{function l(){B(e.el.currentTime),x(e.el.currentTime);}function L(){if(!s)return;let S=e.el.muted||e.el.volume===0;S&&!N?(a(s.trackingEvents.mute),e.emit("ad:mute",{adId:u})):!S&&N&&(a(s.trackingEvents.unmute),e.emit("ad:unmute",{adId:u})),N=S,e.emit("ad:volumeChange",{adId:u,volume:e.el.muted?0:e.el.volume});}function F(){if(!s)return;let S=!!document.fullscreenElement;S&&!w?(a(s.trackingEvents.playerExpand),e.emit("ad:fullscreen",{adId:u,fullscreen:!0})):!S&&w&&(a(s.trackingEvents.playerCollapse),e.emit("ad:fullscreen",{adId:u,fullscreen:!1})),w=S;}function R(){s&&B(s.duration),C(),b(),e.emit("ad:end",{adId:u}),e.el.src=q,e.el.load(),e.el.currentTime=T,D||e.el.play().catch(()=>{e.el.muted=!0,e.el.play().catch(()=>{});}),m();}function C(){e.el.removeEventListener("timeupdate",l),e.el.removeEventListener("ended",R),e.el.removeEventListener("canplay",P),e.el.removeEventListener("volumechange",L),document.removeEventListener("fullscreenchange",F),o=null;}function P(){e.el.removeEventListener("canplay",P),s&&(a(s.trackingEvents.loaded),a(s.trackingEvents.creativeView),e.emit("ad:loaded",{adId:u}),f("ad:playing"),e.el.play().catch(()=>{e.el.muted=!0,e.el.play().catch(()=>{});}));}e.el.addEventListener("canplay",P),e.el.addEventListener("timeupdate",l),e.el.addEventListener("ended",R),e.el.addEventListener("volumechange",L),document.addEventListener("fullscreenchange",F),o=C,e.el.src=E.url,e.el.load();});}catch(p){i||(a(n.trackingEvents.error),e.emit("ad:error",{error:p instanceof Error?p:new Error(String(p))}));}finally{a(n.trackingEvents.breakEnd),e.emit("ad:breakEnd",{breakId:n.breakId}),r&&r.resume();}}}async function g(){if(!i)try{let n=t.timeout??1e4,v=new AbortController,k=setTimeout(()=>v.abort(),n),p;try{let u=await fetch(t.url,{signal:v.signal});if(!u.ok)throw new Error(`VMAP fetch failed: ${u.status}`);p=await u.text();}finally{clearTimeout(k);}if(i)return;let s=O(p);if(s.adBreaks.length===0)return;let A=V(e,s.adBreaks,u=>c$2(u));r=A,A.start();}catch(n){if(i)return;e.emit("ad:error",{error:n instanceof Error?n:new Error(String(n))});}}function d({to:n}){n==="ready"&&!i&&(e.off("statechange",d),g());}return e.state==="ready"||e.state==="playing"||e.state==="paused"?g():e.on("statechange",d),()=>{i=true,e.off("statechange",d),r&&r.destroy(),o&&o();}}}}function W(t){if(t.length===0)return null;let e=t.filter(r=>r.mimeType==="video/mp4");return (e.length>0?e:t).sort((r,o)=>(o.bitrate??0)-(r.bitrate??0))[0]}export{V as createScheduler,O as parseVmap,te as vmap};
package/package.json ADDED
@@ -0,0 +1,89 @@
1
+ {
2
+ "name": "@videts/vide",
3
+ "version": "0.7.0",
4
+ "description": "Modular video player — use only what you need. VAST/VMAP, HLS, DASH as plugins.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.mjs"
10
+ },
11
+ "./vast": {
12
+ "types": "./dist/vast/index.d.ts",
13
+ "import": "./dist/vast/index.mjs"
14
+ },
15
+ "./vmap": {
16
+ "types": "./dist/vmap/index.d.ts",
17
+ "import": "./dist/vmap/index.mjs"
18
+ },
19
+ "./omid": {
20
+ "types": "./dist/omid/index.d.ts",
21
+ "import": "./dist/omid/index.mjs"
22
+ },
23
+ "./simid": {
24
+ "types": "./dist/simid/index.d.ts",
25
+ "import": "./dist/simid/index.mjs"
26
+ },
27
+ "./hls": {
28
+ "types": "./dist/hls/index.d.ts",
29
+ "import": "./dist/hls/index.mjs"
30
+ },
31
+ "./dash": {
32
+ "types": "./dist/dash/index.d.ts",
33
+ "import": "./dist/dash/index.mjs"
34
+ },
35
+ "./drm": {
36
+ "types": "./dist/drm/index.d.ts",
37
+ "import": "./dist/drm/index.mjs"
38
+ },
39
+ "./ssai": {
40
+ "types": "./dist/ssai/index.d.ts",
41
+ "import": "./dist/ssai/index.mjs"
42
+ },
43
+ "./ui": {
44
+ "types": "./dist/ui/index.d.ts",
45
+ "import": "./dist/ui/index.mjs"
46
+ },
47
+ "./ui/theme.css": "./dist/ui/theme.css"
48
+ },
49
+ "files": [
50
+ "dist"
51
+ ],
52
+ "sideEffects": false,
53
+ "devDependencies": {
54
+ "@biomejs/biome": "^1.9.0",
55
+ "dashjs": "^4.7.0",
56
+ "hls.js": "^1.6.15",
57
+ "jsdom": "^25.0.0",
58
+ "tsup": "^8.0.0",
59
+ "typescript": "^5.6.0",
60
+ "vitest": "^2.0.0"
61
+ },
62
+ "peerDependencies": {
63
+ "dashjs": "^4.0.0",
64
+ "hls.js": "^1.0.0"
65
+ },
66
+ "peerDependenciesMeta": {
67
+ "dashjs": {
68
+ "optional": true
69
+ },
70
+ "hls.js": {
71
+ "optional": true
72
+ }
73
+ },
74
+ "publishConfig": {
75
+ "access": "public"
76
+ },
77
+ "license": "MIT",
78
+ "engines": {
79
+ "node": ">=18"
80
+ },
81
+ "scripts": {
82
+ "build": "tsup",
83
+ "test": "vitest run",
84
+ "test:watch": "vitest",
85
+ "lint": "biome check src tests",
86
+ "lint:fix": "biome check --write src tests",
87
+ "typecheck": "tsc --noEmit"
88
+ }
89
+ }