stormcloud-video-player 0.1.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 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/player/StormcloudVideoPlayer.ts","../src/sdk/ima.ts","../src/utils/tracking.ts","../src/ui/StormcloudVideoPlayer.tsx"],"sourcesContent":["export { StormcloudVideoPlayer } from \"./player/StormcloudVideoPlayer\";\nexport type {\n StormcloudVideoPlayerConfig,\n AdBreak,\n AdSchedule,\n LateJoinPolicy,\n ClientInfo,\n TrackingData,\n HeartbeatData,\n} from \"./types\";\n\nexport { StormcloudVideoPlayerComponent } from \"./ui/StormcloudVideoPlayer\";\nexport type { StormcloudVideoPlayerProps } from \"./ui/StormcloudVideoPlayer\";\n\nexport {\n getClientInfo,\n getBrowserID,\n sendInitialTracking,\n sendHeartbeat,\n} from \"./utils/tracking\";\n","import Hls from \"hls.js\";\nimport type {\n StormcloudVideoPlayerConfig,\n Scte35Marker,\n Id3TagInfo,\n ImaController,\n AdBreak,\n AdSchedule,\n StormcloudApiResponse,\n} from \"../types\";\nimport { createImaController } from \"../sdk/ima\";\nimport { sendInitialTracking, sendHeartbeat } from \"../utils/tracking\";\n\nexport class StormcloudVideoPlayer {\n private readonly video: HTMLVideoElement;\n private readonly config: StormcloudVideoPlayerConfig;\n private hls?: Hls;\n private ima: ImaController;\n private attached = false;\n private adSchedule: AdSchedule | undefined;\n private inAdBreak = false;\n private currentAdBreakStartWallClockMs: number | undefined;\n private expectedAdBreakDurationMs: number | undefined;\n private adStopTimerId: number | undefined;\n private adStartTimerId: number | undefined;\n private adFailsafeTimerId: number | undefined;\n private ptsDriftEmaMs = 0;\n private adPodQueue: string[] = [];\n private apiVastTagUrl: string | undefined;\n private vastConfig:\n | StormcloudApiResponse[\"response\"][\"options\"][\"vast\"]\n | undefined;\n private lastHeartbeatTime: number = 0;\n private heartbeatInterval: number | undefined;\n private currentAdIndex: number = 0;\n private totalAdsInBreak: number = 0;\n private showAds: boolean = false;\n\n constructor(config: StormcloudVideoPlayerConfig) {\n this.config = config;\n this.video = config.videoElement;\n this.adSchedule = config.adSchedule;\n this.ima = createImaController(this.video);\n }\n\n async load(): Promise<void> {\n if (!this.attached) {\n this.attach();\n }\n\n // Always fetch ad configuration from backend API\n try {\n await this.fetchAdConfiguration();\n } catch (error) {\n if (this.config.debugAdTiming) {\n console.warn(\"[StormcloudVideoPlayer] Failed to fetch ad configuration:\", error);\n }\n }\n\n // Initialize tracking\n this.initializeTracking();\n\n if (this.shouldUseNativeHls()) {\n this.video.src = this.config.src;\n if (this.config.autoplay) {\n await this.video.play().catch(() => {});\n }\n return;\n }\n\n this.hls = new Hls({\n enableWorker: true,\n backBufferLength: 30,\n liveDurationInfinity: true,\n lowLatencyMode: !!this.config.lowLatencyMode,\n maxLiveSyncPlaybackRate: this.config.lowLatencyMode ? 1.5 : 1.0,\n ...(this.config.lowLatencyMode ? { liveSyncDuration: 2 } : {}),\n });\n\n this.hls.on(Hls.Events.MEDIA_ATTACHED, () => {\n this.hls?.loadSource(this.config.src);\n });\n\n this.hls.on(Hls.Events.MANIFEST_PARSED, async () => {\n if (this.config.autoplay) {\n await this.video.play().catch(() => {});\n }\n });\n\n this.hls.on(Hls.Events.FRAG_PARSING_METADATA, (_evt, data: any) => {\n const id3Tags: Id3TagInfo[] = (data?.samples || []).map((s: any) => ({\n key: \"ID3\",\n value: s?.data,\n ptsSeconds: s?.pts,\n }));\n id3Tags.forEach((tag) => this.onId3Tag(tag));\n });\n\n this.hls.on(Hls.Events.FRAG_CHANGED, (_evt, data: any) => {\n const frag = data?.frag;\n const tagList: any[] | undefined = frag?.tagList;\n if (!Array.isArray(tagList)) return;\n\n // tagList can be pairs like [tag, value] or full lines. Handle both.\n for (const entry of tagList) {\n let tag = \"\";\n let value = \"\";\n if (Array.isArray(entry)) {\n tag = String(entry[0] ?? \"\");\n value = String(entry[1] ?? \"\");\n } else if (typeof entry === \"string\") {\n const idx = entry.indexOf(\":\");\n if (idx >= 0) {\n tag = entry.substring(0, idx);\n value = entry.substring(idx + 1);\n } else {\n tag = entry;\n value = \"\";\n }\n }\n\n if (!tag) continue;\n if (tag.includes(\"EXT-X-CUE-OUT\")) {\n const durationSeconds = this.parseCueOutDuration(value);\n const marker: Scte35Marker = {\n type: \"start\",\n ...(durationSeconds !== undefined ? { durationSeconds } : {}),\n raw: { tag, value },\n } as Scte35Marker;\n this.onScte35Marker(marker);\n } else if (tag.includes(\"EXT-X-CUE-OUT-CONT\")) {\n const prog = this.parseCueOutCont(value);\n const marker: Scte35Marker = {\n type: \"progress\",\n ...(prog?.duration !== undefined\n ? { durationSeconds: prog.duration }\n : {}),\n ...(prog?.elapsed !== undefined\n ? { ptsSeconds: prog.elapsed }\n : {}),\n raw: { tag, value },\n } as Scte35Marker;\n this.onScte35Marker(marker);\n } else if (tag.includes(\"EXT-X-CUE-IN\")) {\n this.onScte35Marker({ type: \"end\", raw: { tag, value } });\n } else if (tag.includes(\"EXT-X-DATERANGE\")) {\n const attrs = this.parseAttributeList(value);\n const hasScteOut =\n \"SCTE35-OUT\" in attrs || attrs[\"SCTE35-OUT\"] !== undefined;\n const hasScteIn =\n \"SCTE35-IN\" in attrs || attrs[\"SCTE35-IN\"] !== undefined;\n const klass = String(attrs[\"CLASS\"] ?? \"\");\n const duration = this.toNumber(attrs[\"DURATION\"]);\n\n if (hasScteOut || /com\\.apple\\.hls\\.cue/i.test(klass)) {\n const marker: Scte35Marker = {\n type: \"start\",\n ...(duration !== undefined ? { durationSeconds: duration } : {}),\n raw: { tag, value, attrs },\n } as Scte35Marker;\n this.onScte35Marker(marker);\n }\n if (hasScteIn) {\n this.onScte35Marker({ type: \"end\", raw: { tag, value, attrs } });\n }\n }\n }\n });\n\n this.hls.on(Hls.Events.ERROR, (_evt, data) => {\n if (data?.fatal) {\n switch (data.type) {\n case Hls.ErrorTypes.NETWORK_ERROR:\n this.hls?.startLoad();\n break;\n case Hls.ErrorTypes.MEDIA_ERROR:\n this.hls?.recoverMediaError();\n break;\n default:\n this.destroy();\n break;\n }\n }\n });\n\n this.hls.attachMedia(this.video);\n }\n\n private attach(): void {\n if (this.attached) return;\n this.attached = true;\n this.video.autoplay = !!this.config.autoplay;\n this.video.muted = !!this.config.muted;\n\n this.ima.initialize();\n this.ima.on(\"all_ads_completed\", () => {\n if (!this.inAdBreak) return;\n const remaining = this.getRemainingAdMs();\n if (remaining > 500 && this.adPodQueue.length > 0) {\n const next = this.adPodQueue.shift()!;\n this.currentAdIndex++; // Increment before playing next ad (like showVastOnlyHandler)\n this.playSingleAd(next).catch(() => {});\n } else {\n // Reset ad counters when ad break is complete\n this.currentAdIndex = 0;\n this.totalAdsInBreak = 0;\n this.showAds = false;\n }\n });\n this.ima.on(\"ad_error\", () => {\n if (this.config.debugAdTiming) {\n console.log(\"[StormcloudVideoPlayer] IMA ad_error event received\");\n }\n if (!this.inAdBreak) return;\n const remaining = this.getRemainingAdMs();\n if (remaining > 500 && this.adPodQueue.length > 0) {\n const next = this.adPodQueue.shift()!;\n this.currentAdIndex++; // Increment before playing next ad (like showVastOnlyHandler)\n this.playSingleAd(next).catch(() => {});\n } else {\n // No more ads to try, resume content\n this.handleAdFailure();\n }\n });\n this.ima.on(\"content_pause\", () => {\n if (this.config.debugAdTiming) {\n console.log(\"[StormcloudVideoPlayer] IMA content_pause event received\");\n }\n // Ad is starting successfully, clear failsafe timer\n this.clearAdFailsafeTimer();\n });\n this.ima.on(\"content_resume\", () => {\n if (this.config.debugAdTiming) {\n console.log(\"[StormcloudVideoPlayer] IMA content_resume event received\");\n }\n // Ad finished successfully, clear failsafe timer\n this.clearAdFailsafeTimer();\n });\n\n this.video.addEventListener(\"timeupdate\", () => {\n this.onTimeUpdate(this.video.currentTime);\n });\n }\n\n private shouldUseNativeHls(): boolean {\n const canNative = this.video.canPlayType(\"application/vnd.apple.mpegURL\");\n return !!(this.config.allowNativeHls && canNative);\n }\n\n private onId3Tag(tag: Id3TagInfo): void {\n // Parse ID3 for SCTE-35 markers (implementation-specific per feed)\n if (typeof tag.ptsSeconds === \"number\") {\n this.updatePtsDrift(tag.ptsSeconds);\n }\n const marker = this.parseScte35FromId3(tag);\n if (marker) {\n this.onScte35Marker(marker);\n }\n }\n\n private parseScte35FromId3(tag: Id3TagInfo): Scte35Marker | undefined {\n const text = this.decodeId3ValueToText(tag.value);\n if (!text) return undefined;\n\n // Handle common in-band cues encoded as ID3 text\n const cueOutMatch =\n text.match(/EXT-X-CUE-OUT(?::([^\\r\\n]*))?/i) ||\n text.match(/CUE-OUT(?::([^\\r\\n]*))?/i);\n if (cueOutMatch) {\n const arg = (cueOutMatch[1] ?? \"\").trim();\n const dur = this.parseCueOutDuration(arg);\n const marker: Scte35Marker = {\n type: \"start\",\n ...(tag.ptsSeconds !== undefined ? { ptsSeconds: tag.ptsSeconds } : {}),\n ...(dur !== undefined ? { durationSeconds: dur } : {}),\n raw: { id3: text },\n } as Scte35Marker;\n return marker;\n }\n\n const cueOutContMatch = text.match(/EXT-X-CUE-OUT-CONT:([^\\r\\n]*)/i);\n if (cueOutContMatch) {\n const arg = (cueOutContMatch[1] ?? \"\").trim();\n const cont = this.parseCueOutCont(arg);\n const marker: Scte35Marker = {\n type: \"progress\",\n ...(tag.ptsSeconds !== undefined ? { ptsSeconds: tag.ptsSeconds } : {}),\n ...(cont?.duration !== undefined\n ? { durationSeconds: cont.duration }\n : {}),\n raw: { id3: text },\n } as Scte35Marker;\n return marker;\n }\n\n const cueInMatch = text.match(/EXT-X-CUE-IN\\b/i) || text.match(/CUE-IN\\b/i);\n if (cueInMatch) {\n const marker: Scte35Marker = {\n type: \"end\",\n ...(tag.ptsSeconds !== undefined ? { ptsSeconds: tag.ptsSeconds } : {}),\n raw: { id3: text },\n } as Scte35Marker;\n return marker;\n }\n\n const daterangeMatch = text.match(/EXT-X-DATERANGE:([^\\r\\n]*)/i);\n if (daterangeMatch) {\n const attrs = this.parseAttributeList(daterangeMatch[1] ?? \"\");\n const hasScteOut =\n \"SCTE35-OUT\" in attrs || attrs[\"SCTE35-OUT\"] !== undefined;\n const hasScteIn =\n \"SCTE35-IN\" in attrs || attrs[\"SCTE35-IN\"] !== undefined;\n const klass = String(attrs[\"CLASS\"] ?? \"\");\n const duration = this.toNumber(attrs[\"DURATION\"]);\n if (hasScteOut || /com\\.apple\\.hls\\.cue/i.test(klass)) {\n const marker: Scte35Marker = {\n type: \"start\",\n ...(tag.ptsSeconds !== undefined\n ? { ptsSeconds: tag.ptsSeconds }\n : {}),\n ...(duration !== undefined ? { durationSeconds: duration } : {}),\n raw: { id3: text, attrs },\n } as Scte35Marker;\n return marker;\n }\n if (hasScteIn) {\n const marker: Scte35Marker = {\n type: \"end\",\n ...(tag.ptsSeconds !== undefined\n ? { ptsSeconds: tag.ptsSeconds }\n : {}),\n raw: { id3: text, attrs },\n } as Scte35Marker;\n return marker;\n }\n }\n\n // Some providers embed plain tokens\n if (/SCTE35-OUT/i.test(text)) {\n const marker: Scte35Marker = {\n type: \"start\",\n ...(tag.ptsSeconds !== undefined ? { ptsSeconds: tag.ptsSeconds } : {}),\n raw: { id3: text },\n } as Scte35Marker;\n return marker;\n }\n if (/SCTE35-IN/i.test(text)) {\n const marker: Scte35Marker = {\n type: \"end\",\n ...(tag.ptsSeconds !== undefined ? { ptsSeconds: tag.ptsSeconds } : {}),\n raw: { id3: text },\n } as Scte35Marker;\n return marker;\n }\n\n // Attempt binary SCTE-35 parsing if the ID3 value is binary\n if (tag.value instanceof Uint8Array) {\n const bin = this.parseScte35Binary(tag.value);\n if (bin) return bin;\n }\n\n return undefined;\n }\n\n private decodeId3ValueToText(value: string | Uint8Array): string | undefined {\n try {\n if (typeof value === \"string\") return value;\n // Try UTF-8 decoding\n const decoder = new TextDecoder(\"utf-8\", { fatal: false });\n const text = decoder.decode(value);\n if (text && /[\\x20-\\x7E]/.test(text)) return text;\n // Fallback to latin-1\n let out = \"\";\n for (let i = 0; i < value.length; i++)\n out += String.fromCharCode(value[i]!);\n return out;\n } catch {\n return undefined;\n }\n }\n\n private onScte35Marker(marker: Scte35Marker): void {\n if (this.config.debugAdTiming) {\n console.log(\"[StormcloudVideoPlayer] SCTE-35 marker detected:\", {\n type: marker.type,\n ptsSeconds: marker.ptsSeconds,\n durationSeconds: marker.durationSeconds,\n currentTime: this.video.currentTime,\n raw: marker.raw,\n });\n }\n\n if (marker.type === \"start\") {\n this.inAdBreak = true;\n const durationMs =\n marker.durationSeconds != null\n ? marker.durationSeconds * 1000\n : undefined;\n this.expectedAdBreakDurationMs = durationMs;\n this.currentAdBreakStartWallClockMs = Date.now();\n\n // Respect skip_to_content policy for live-tune scenarios\n const policy = this.adSchedule?.lateJoinPolicy ?? \"play_remaining\";\n if (policy !== \"skip_to_content\") {\n // Check if this is a manifest-based marker (EXT-X-CUE-OUT, EXT-X-DATERANGE)\n const isManifestMarker = this.isManifestBasedMarker(marker);\n const forceImmediate = this.config.immediateManifestAds ?? true; // Default to true for better UX\n\n if (this.config.debugAdTiming) {\n console.log(\"[StormcloudVideoPlayer] Ad start decision:\", {\n isManifestMarker,\n forceImmediate,\n hasPts: typeof marker.ptsSeconds === \"number\",\n policy,\n });\n }\n\n if (isManifestMarker && forceImmediate) {\n // Manifest markers are already synchronized to segment boundaries\n // Start ad immediately without PTS-based delay\n if (this.config.debugAdTiming) {\n console.log(\"[StormcloudVideoPlayer] Starting ad immediately (manifest-based)\");\n }\n this.clearAdStartTimer();\n this.handleAdStart(marker);\n } else if (typeof marker.ptsSeconds === \"number\") {\n // ID3-based markers may need PTS synchronization\n const tol = this.config.driftToleranceMs ?? 1000;\n const nowMs = this.video.currentTime * 1000;\n const estCurrentPtsMs = nowMs - this.ptsDriftEmaMs;\n const deltaMs = Math.floor(\n marker.ptsSeconds * 1000 - estCurrentPtsMs\n );\n\n if (this.config.debugAdTiming) {\n console.log(\"[StormcloudVideoPlayer] PTS-based timing calculation:\", {\n nowMs,\n estCurrentPtsMs,\n markerPtsMs: marker.ptsSeconds * 1000,\n deltaMs,\n tolerance: tol,\n });\n }\n\n if (deltaMs > tol) {\n if (this.config.debugAdTiming) {\n console.log(`[StormcloudVideoPlayer] Scheduling ad start in ${deltaMs}ms`);\n }\n this.scheduleAdStartIn(deltaMs);\n } else {\n if (this.config.debugAdTiming) {\n console.log(\n \"[StormcloudVideoPlayer] Starting ad immediately (within tolerance)\"\n );\n }\n this.clearAdStartTimer();\n this.handleAdStart(marker);\n }\n } else {\n // Fallback: start immediately\n if (this.config.debugAdTiming) {\n console.log(\"[StormcloudVideoPlayer] Starting ad immediately (fallback)\");\n }\n this.clearAdStartTimer();\n this.handleAdStart(marker);\n }\n }\n if (this.expectedAdBreakDurationMs != null) {\n this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);\n }\n return;\n }\n if (marker.type === \"progress\" && this.inAdBreak) {\n // If we joined mid-ad, estimate remaining if we have duration\n if (marker.durationSeconds != null) {\n this.expectedAdBreakDurationMs = marker.durationSeconds * 1000;\n }\n if (\n this.expectedAdBreakDurationMs != null &&\n this.currentAdBreakStartWallClockMs != null\n ) {\n const elapsedMs = Date.now() - this.currentAdBreakStartWallClockMs;\n const remainingMs = Math.max(\n 0,\n this.expectedAdBreakDurationMs - elapsedMs\n );\n this.scheduleAdStopCountdown(remainingMs);\n }\n // If ad not playing yet and policy allows, start remaining\n if (!this.ima.isAdPlaying()) {\n const policy = this.adSchedule?.lateJoinPolicy ?? \"play_remaining\";\n const scheduled = this.findCurrentOrNextBreak(\n this.video.currentTime * 1000\n );\n const tags =\n this.selectVastTagsForBreak(scheduled) ||\n (this.apiVastTagUrl ? [this.apiVastTagUrl] : undefined);\n if (policy === \"play_remaining\" && tags && tags.length > 0) {\n const first = tags[0] as string;\n const rest = tags.slice(1);\n this.adPodQueue = rest;\n this.playSingleAd(first).catch(() => {});\n }\n }\n return;\n }\n if (marker.type === \"end\") {\n this.inAdBreak = false;\n this.expectedAdBreakDurationMs = undefined;\n this.currentAdBreakStartWallClockMs = undefined;\n this.clearAdStartTimer();\n this.clearAdStopTimer();\n // Stop ads if currently playing\n if (this.ima.isAdPlaying()) {\n this.ima.stop().catch(() => {});\n }\n return;\n }\n }\n\n private parseCueOutDuration(value: string): number | undefined {\n // Examples: \"30\" or \"DURATION=30\" or \"Duration=30.0\"\n const num = parseFloat(value.trim());\n if (!Number.isNaN(num)) return num;\n const match =\n value.match(/(?:^|[,\\s])DURATION\\s*=\\s*([0-9.]+)/i) ||\n value.match(/Duration\\s*=\\s*([0-9.]+)/i);\n if (match && match[1] != null) {\n const dStr = match[1];\n const d = parseFloat(dStr);\n return Number.isNaN(d) ? undefined : d;\n }\n return undefined;\n }\n\n private parseCueOutCont(\n value: string\n ): { elapsed?: number; duration?: number } | undefined {\n const elapsedMatch = value.match(/Elapsed\\s*=\\s*([0-9.]+)/i);\n const durationMatch = value.match(/Duration\\s*=\\s*([0-9.]+)/i);\n const res: { elapsed?: number; duration?: number } = {};\n if (elapsedMatch && elapsedMatch[1] != null) {\n const e = parseFloat(elapsedMatch[1]);\n if (!Number.isNaN(e)) res.elapsed = e;\n }\n if (durationMatch && durationMatch[1] != null) {\n const d = parseFloat(durationMatch[1]);\n if (!Number.isNaN(d)) res.duration = d;\n }\n if (\"elapsed\" in res || \"duration\" in res) return res;\n return undefined;\n }\n\n private parseAttributeList(value: string): Record<string, string> {\n const attrs: Record<string, string> = {};\n const regex = /([A-Z0-9-]+)=((\"[^\"]*\")|([^\",]*))(?:,|$)/gi;\n let match: RegExpExecArray | null;\n while ((match = regex.exec(value)) !== null) {\n const key: string = (match[1] ?? \"\") as string;\n let rawVal: string = (match[3] ?? match[4] ?? \"\") as string;\n if (rawVal.startsWith('\"') && rawVal.endsWith('\"')) {\n rawVal = rawVal.slice(1, -1);\n }\n if (key) {\n attrs[key] = rawVal;\n }\n }\n return attrs;\n }\n\n private toNumber(val: unknown): number | undefined {\n if (val == null) return undefined;\n const n = typeof val === \"string\" ? parseFloat(val) : Number(val);\n return Number.isNaN(n) ? undefined : n;\n }\n\n private isManifestBasedMarker(marker: Scte35Marker): boolean {\n // Check if this marker came from HLS manifest tags rather than ID3 metadata\n const raw = marker.raw as any;\n if (!raw) return false;\n\n // Manifest-based markers have 'tag' property from HLS tagList parsing\n if (raw.tag) {\n const tag = String(raw.tag);\n return (\n tag.includes(\"EXT-X-CUE-OUT\") ||\n tag.includes(\"EXT-X-CUE-IN\") ||\n tag.includes(\"EXT-X-DATERANGE\")\n );\n }\n\n // ID3-based markers have 'id3' property\n if (raw.id3) return false;\n\n // Binary SCTE-35 markers have splice_command_type\n if (raw.splice_command_type) return false;\n\n // Default to false for unknown marker types\n return false;\n }\n\n private parseScte35Binary(data: Uint8Array): Scte35Marker | undefined {\n class BitReader {\n private bytePos = 0;\n private bitPos = 0; // 0..7\n constructor(private readonly buf: Uint8Array) {}\n readBits(numBits: number): number {\n let result = 0;\n while (numBits > 0) {\n if (this.bytePos >= this.buf.length) return result;\n const remainingInByte = 8 - this.bitPos;\n const toRead = Math.min(numBits, remainingInByte);\n const currentByte = this.buf[this.bytePos]!;\n const shift = remainingInByte - toRead;\n const mask = ((1 << toRead) - 1) & 0xff;\n const bits = (currentByte >> shift) & mask;\n result = (result << toRead) | bits;\n this.bitPos += toRead;\n if (this.bitPos >= 8) {\n this.bitPos = 0;\n this.bytePos += 1;\n }\n numBits -= toRead;\n }\n return result >>> 0;\n }\n skipBits(n: number): void {\n this.readBits(n);\n }\n }\n\n const r = new BitReader(data);\n const tableId = r.readBits(8);\n if (tableId !== 0xfc) return undefined;\n r.readBits(1);\n r.readBits(1);\n r.readBits(2);\n const sectionLength = r.readBits(12);\n r.readBits(8);\n r.readBits(1);\n r.readBits(6);\n const ptsAdjHigh = r.readBits(1);\n const ptsAdjLow = r.readBits(32);\n void ptsAdjHigh;\n void ptsAdjLow;\n r.readBits(8);\n r.readBits(12);\n const spliceCommandLength = r.readBits(12);\n const spliceCommandType = r.readBits(8);\n if (spliceCommandType !== 5) {\n return undefined;\n }\n r.readBits(32);\n const cancel = r.readBits(1) === 1;\n r.readBits(7);\n if (cancel) return undefined;\n const outOfNetwork = r.readBits(1) === 1;\n const programSpliceFlag = r.readBits(1) === 1;\n const durationFlag = r.readBits(1) === 1;\n const spliceImmediateFlag = r.readBits(1) === 1;\n r.readBits(4);\n if (programSpliceFlag && !spliceImmediateFlag) {\n const timeSpecifiedFlag = r.readBits(1) === 1;\n if (timeSpecifiedFlag) {\n r.readBits(6);\n r.readBits(33);\n } else {\n r.readBits(7);\n }\n } else if (!programSpliceFlag) {\n const componentCount = r.readBits(8);\n for (let i = 0; i < componentCount; i++) {\n r.readBits(8);\n if (!spliceImmediateFlag) {\n const timeSpecifiedFlag = r.readBits(1) === 1;\n if (timeSpecifiedFlag) {\n r.readBits(6);\n r.readBits(33);\n } else {\n r.readBits(7);\n }\n }\n }\n }\n let durationSeconds: number | undefined = undefined;\n if (durationFlag) {\n r.readBits(6);\n r.readBits(1);\n const high = r.readBits(1);\n const low = r.readBits(32);\n const durationTicks = high * 0x100000000 + low;\n durationSeconds = durationTicks / 90000;\n }\n r.readBits(16);\n r.readBits(8);\n r.readBits(8);\n\n if (outOfNetwork) {\n const marker: Scte35Marker = {\n type: \"start\",\n ...(durationSeconds !== undefined ? { durationSeconds } : {}),\n raw: { splice_command_type: 5 },\n } as Scte35Marker;\n return marker;\n }\n return undefined;\n }\n\n private initializeTracking(): void {\n // Send initial tracking data\n sendInitialTracking().catch((error) => {\n if (this.config.debugAdTiming) {\n console.warn(\"[StormcloudVideoPlayer] Failed to send initial tracking:\", error);\n }\n });\n\n // Set up heartbeat interval (every 10 seconds)\n this.heartbeatInterval = window.setInterval(() => {\n this.sendHeartbeatIfNeeded();\n }, 1000); // Check every second, but only send every 10 seconds\n }\n\n private sendHeartbeatIfNeeded(): void {\n const now = Date.now();\n if (!this.lastHeartbeatTime || now - this.lastHeartbeatTime > 10000) {\n this.lastHeartbeatTime = now;\n sendHeartbeat().catch((error) => {\n if (this.config.debugAdTiming) {\n console.warn(\"[StormcloudVideoPlayer] Failed to send heartbeat:\", error);\n }\n });\n }\n }\n\n private async fetchAdConfiguration(): Promise<void> {\n const apiUrl = \"https://stormcloud.co/api-stormcloud-dev/stormcloud/ads/web\";\n\n if (this.config.debugAdTiming) {\n console.log(\"[StormcloudVideoPlayer] Fetching ad configuration from:\", apiUrl);\n }\n\n const response = await fetch(apiUrl);\n if (!response.ok) {\n throw new Error(`Failed to fetch ad configuration: ${response.status}`);\n }\n\n const data: StormcloudApiResponse = await response.json();\n\n // Extract VAST tag URL from IMA payload\n const imaPayload = data.response?.ima?.[\"publisherdesk.ima\"]?.payload;\n if (imaPayload) {\n this.apiVastTagUrl = decodeURIComponent(imaPayload);\n if (this.config.debugAdTiming) {\n console.log(\"[StormcloudVideoPlayer] Extracted VAST tag URL:\", this.apiVastTagUrl);\n }\n }\n\n // Store VAST configuration\n this.vastConfig = data.response?.options?.vast;\n\n if (this.config.debugAdTiming) {\n console.log(\"[StormcloudVideoPlayer] Ad configuration loaded:\", {\n vastTagUrl: this.apiVastTagUrl,\n vastConfig: this.vastConfig,\n });\n }\n }\n\n // Public helpers to manage schedule and external ingestion\n setAdSchedule(schedule: AdSchedule | undefined): void {\n this.adSchedule = schedule;\n }\n\n // Public methods to get ad status\n getCurrentAdIndex(): number {\n return this.currentAdIndex;\n }\n\n getTotalAdsInBreak(): number {\n return this.totalAdsInBreak;\n }\n\n isAdPlaying(): boolean {\n return this.inAdBreak && this.ima.isAdPlaying();\n }\n\n isShowingAds(): boolean {\n return this.showAds;\n }\n\n getStreamType(): \"hls\" | \"other\" {\n const url = this.config.src.toLowerCase();\n if (\n url.includes(\".m3u8\") ||\n url.includes(\"/hls/\") ||\n url.includes(\"application/vnd.apple.mpegurl\")\n ) {\n return \"hls\";\n }\n return \"other\";\n }\n\n shouldShowNativeControls(): boolean {\n // Native controls are shown for non-HLS streams (like the original Showfer Player)\n return this.getStreamType() !== \"hls\";\n }\n\n async loadAdScheduleFromUrl(url: string): Promise<void> {\n const res = await fetch(url);\n if (!res.ok) throw new Error(`Failed to fetch ad schedule: ${res.status}`);\n const data = await res.json();\n // Expect data to match AdSchedule. Implement adapters as needed.\n this.adSchedule = data as AdSchedule;\n }\n\n async loadDefaultVastFromAiry(\n airyApiUrl: string,\n params?: Record<string, string>\n ): Promise<void> {\n const usp = new URLSearchParams(params || {});\n const url = `${airyApiUrl}?${usp.toString()}`;\n const res = await fetch(url);\n if (!res.ok) throw new Error(`Failed to fetch Airy ads: ${res.status}`);\n const data = await res.json();\n // If API returns a tag URL under known fields; adjust mapping as necessary\n const tag = data?.adTagUrl || data?.vastTagUrl || data?.tagUrl;\n if (typeof tag === \"string\" && tag.length > 0) {\n this.apiVastTagUrl = tag;\n }\n }\n\n private async handleAdStart(_marker: Scte35Marker): Promise<void> {\n const scheduled = this.findCurrentOrNextBreak(\n this.video.currentTime * 1000\n );\n const tags = this.selectVastTagsForBreak(scheduled);\n\n // Determine which VAST tag to use (priority: API > scheduled)\n let vastTagUrl: string | undefined;\n let adsNumber = 1;\n\n if (this.apiVastTagUrl) {\n // Use API-provided VAST tag (primary source)\n vastTagUrl = this.apiVastTagUrl;\n\n // Determine number of ads based on stream type and configuration\n if (this.vastConfig) {\n const isHls =\n this.config.src.includes(\".m3u8\") || this.config.src.includes(\"hls\");\n if (isHls && this.vastConfig.cue_tones?.number_ads) {\n adsNumber = this.vastConfig.cue_tones.number_ads;\n } else if (!isHls && this.vastConfig.timer_vod?.number_ads) {\n adsNumber = this.vastConfig.timer_vod.number_ads;\n }\n }\n\n // Create ad pod queue for multiple ads\n this.adPodQueue = new Array(adsNumber - 1).fill(vastTagUrl);\n this.currentAdIndex = 0; // Start at 0, will increment to 1 when first ad starts\n this.totalAdsInBreak = adsNumber;\n\n if (this.config.debugAdTiming) {\n console.log(\n `[StormcloudVideoPlayer] Using API VAST tag with ${adsNumber} ads:`,\n vastTagUrl\n );\n }\n } else if (tags && tags.length > 0) {\n // Fallback to scheduled ad tags\n vastTagUrl = tags[0] as string;\n const rest = tags.slice(1);\n this.adPodQueue = rest;\n this.currentAdIndex = 0; // Start at 0, will increment to 1 when first ad starts\n this.totalAdsInBreak = tags.length;\n\n if (this.config.debugAdTiming) {\n console.log(\"[StormcloudVideoPlayer] Using scheduled VAST tag:\", vastTagUrl);\n }\n } else {\n if (this.config.debugAdTiming) {\n console.log(\"[StormcloudVideoPlayer] No VAST tag available for ad\");\n }\n return;\n }\n\n if (vastTagUrl) {\n this.showAds = true;\n this.currentAdIndex++; // Increment before playing (like original: adsAreReproduced + 1)\n await this.playSingleAd(vastTagUrl);\n }\n // If schedule provides duration and we don't have it, use schedule to compute stop\n if (\n this.expectedAdBreakDurationMs == null &&\n scheduled?.durationMs != null\n ) {\n this.expectedAdBreakDurationMs = scheduled.durationMs;\n this.currentAdBreakStartWallClockMs =\n this.currentAdBreakStartWallClockMs ?? Date.now();\n this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);\n }\n }\n\n private findCurrentOrNextBreak(nowMs: number): AdBreak | undefined {\n const schedule = this.adSchedule?.breaks || [];\n let candidate: AdBreak | undefined;\n for (const b of schedule) {\n const tol = this.config.driftToleranceMs ?? 1000;\n if (\n b.startTimeMs <= nowMs + tol &&\n (candidate == null || b.startTimeMs > (candidate.startTimeMs || 0))\n ) {\n candidate = b;\n }\n }\n return candidate;\n }\n\n private onTimeUpdate(currentTimeSec: number): void {\n if (!this.adSchedule || this.ima.isAdPlaying()) return;\n const nowMs = currentTimeSec * 1000;\n const breakToPlay = this.findBreakForTime(nowMs);\n if (breakToPlay) {\n this.handleMidAdJoin(breakToPlay, nowMs);\n }\n }\n\n private async handleMidAdJoin(\n adBreak: AdBreak,\n nowMs: number\n ): Promise<void> {\n const durationMs = adBreak.durationMs ?? 0;\n const endMs = adBreak.startTimeMs + durationMs;\n if (durationMs > 0 && nowMs > adBreak.startTimeMs && nowMs < endMs) {\n const remainingMs = endMs - nowMs;\n const policy = this.adSchedule?.lateJoinPolicy ?? \"play_remaining\";\n if (policy === \"skip_to_content\") return;\n const tags =\n this.selectVastTagsForBreak(adBreak) ||\n (this.apiVastTagUrl ? [this.apiVastTagUrl] : undefined);\n if (policy === \"play_remaining\" && tags && tags.length > 0) {\n const first = tags[0] as string;\n const rest = tags.slice(1);\n this.adPodQueue = rest;\n await this.playSingleAd(first);\n // Start countdown for remaining time\n this.inAdBreak = true;\n this.expectedAdBreakDurationMs = remainingMs;\n this.currentAdBreakStartWallClockMs = Date.now();\n this.scheduleAdStopCountdown(remainingMs);\n }\n }\n }\n\n private scheduleAdStopCountdown(remainingMs: number): void {\n this.clearAdStopTimer();\n const ms = Math.max(0, Math.floor(remainingMs));\n if (ms === 0) {\n this.ensureAdStoppedByTimer();\n return;\n }\n this.adStopTimerId = window.setTimeout(() => {\n this.ensureAdStoppedByTimer();\n }, ms) as unknown as number;\n }\n\n private clearAdStopTimer(): void {\n if (this.adStopTimerId != null) {\n clearTimeout(this.adStopTimerId);\n this.adStopTimerId = undefined;\n }\n }\n\n private ensureAdStoppedByTimer(): void {\n if (!this.inAdBreak) return;\n // If CUE-IN never arrived, stop ad playback when expected time elapses\n this.inAdBreak = false;\n this.expectedAdBreakDurationMs = undefined;\n this.currentAdBreakStartWallClockMs = undefined;\n this.adStopTimerId = undefined;\n if (this.ima.isAdPlaying()) {\n this.ima.stop().catch(() => {});\n }\n }\n\n private scheduleAdStartIn(delayMs: number): void {\n this.clearAdStartTimer();\n const ms = Math.max(0, Math.floor(delayMs));\n if (ms === 0) {\n this.handleAdStart({ type: \"start\" } as Scte35Marker).catch(() => {});\n return;\n }\n this.adStartTimerId = window.setTimeout(() => {\n this.handleAdStart({ type: \"start\" } as Scte35Marker).catch(() => {});\n }, ms) as unknown as number;\n }\n\n private clearAdStartTimer(): void {\n if (this.adStartTimerId != null) {\n clearTimeout(this.adStartTimerId);\n this.adStartTimerId = undefined;\n }\n }\n\n private updatePtsDrift(ptsSecondsSample: number): void {\n const sampleMs = (this.video.currentTime - ptsSecondsSample) * 1000;\n // Clamp unreasonable samples\n if (!Number.isFinite(sampleMs) || Math.abs(sampleMs) > 60000) return;\n const alpha = 0.1;\n this.ptsDriftEmaMs = this.ptsDriftEmaMs * (1 - alpha) + sampleMs * alpha;\n }\n\n private async playSingleAd(vastTagUrl: string): Promise<void> {\n if (this.config.debugAdTiming) {\n console.log(\"[StormcloudVideoPlayer] Attempting to play ad:\", vastTagUrl);\n }\n\n // Set up failsafe timer to prevent video from being paused forever\n this.startAdFailsafeTimer();\n\n try {\n await this.ima.requestAds(vastTagUrl);\n await this.ima.play();\n\n if (this.config.debugAdTiming) {\n console.log(\"[StormcloudVideoPlayer] Ad playback started successfully\");\n }\n } catch (error) {\n if (this.config.debugAdTiming) {\n console.error(\"[StormcloudVideoPlayer] Ad playback failed:\", error);\n }\n\n // If ad fails, resume content immediately\n this.handleAdFailure();\n }\n }\n\n private handleAdFailure(): void {\n if (this.config.debugAdTiming) {\n console.log(\"[StormcloudVideoPlayer] Handling ad failure - resuming content\");\n }\n\n // Clean up ad break state\n this.inAdBreak = false;\n this.expectedAdBreakDurationMs = undefined;\n this.currentAdBreakStartWallClockMs = undefined;\n this.clearAdStartTimer();\n this.clearAdStopTimer();\n this.clearAdFailsafeTimer();\n this.adPodQueue = [];\n this.showAds = false;\n this.currentAdIndex = 0;\n this.totalAdsInBreak = 0;\n\n // Force video to resume\n if (this.video.paused) {\n this.video.play().catch(() => {\n if (this.config.debugAdTiming) {\n console.error(\"[StormcloudVideoPlayer] Failed to resume video after ad failure\");\n }\n });\n }\n }\n\n private startAdFailsafeTimer(): void {\n this.clearAdFailsafeTimer();\n\n // Failsafe: if video is still paused after timeout, force resume\n const failsafeMs = this.config.adFailsafeTimeoutMs ?? 10000;\n\n if (this.config.debugAdTiming) {\n console.log(`[StormcloudVideoPlayer] Starting failsafe timer (${failsafeMs}ms)`);\n }\n\n this.adFailsafeTimerId = window.setTimeout(() => {\n if (this.video.paused) {\n if (this.config.debugAdTiming) {\n console.warn(\n \"[StormcloudVideoPlayer] Failsafe timer triggered - forcing video resume\"\n );\n }\n this.handleAdFailure();\n }\n }, failsafeMs) as unknown as number;\n }\n\n private clearAdFailsafeTimer(): void {\n if (this.adFailsafeTimerId != null) {\n clearTimeout(this.adFailsafeTimerId);\n this.adFailsafeTimerId = undefined;\n }\n }\n\n private selectVastTagsForBreak(b?: AdBreak): string[] | undefined {\n if (!b || !b.vastTagUrl) return undefined;\n if (b.vastTagUrl.includes(\",\")) {\n return b.vastTagUrl\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n }\n return [b.vastTagUrl];\n }\n\n private getRemainingAdMs(): number {\n if (\n this.expectedAdBreakDurationMs == null ||\n this.currentAdBreakStartWallClockMs == null\n )\n return 0;\n const elapsed = Date.now() - this.currentAdBreakStartWallClockMs;\n return Math.max(0, this.expectedAdBreakDurationMs - elapsed);\n }\n\n private findBreakForTime(nowMs: number): AdBreak | undefined {\n const schedule = this.adSchedule?.breaks || [];\n for (const b of schedule) {\n const end = (b.startTimeMs || 0) + (b.durationMs || 0);\n if (\n nowMs >= (b.startTimeMs || 0) &&\n (b.durationMs ? nowMs < end : true)\n ) {\n return b;\n }\n }\n return undefined;\n }\n\n destroy(): void {\n this.clearAdStartTimer();\n this.clearAdStopTimer();\n this.clearAdFailsafeTimer();\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = undefined;\n }\n this.hls?.destroy();\n this.ima?.destroy();\n }\n}\n","import type { ImaController } from \"../types\";\n\ndeclare global {\n interface Window {\n google?: any;\n }\n}\n\nexport function createImaController(video: HTMLVideoElement): ImaController {\n let adPlaying = false;\n const listeners = new Map<string, Set<(payload?: any) => void>>();\n\n function emit(event: string, payload?: any): void {\n const set = listeners.get(event);\n if (!set) return;\n for (const fn of Array.from(set)) {\n try {\n fn(payload);\n } catch {}\n }\n }\n\n function ensureImaLoaded(): Promise<void> {\n // Detect if the current document is inside a sandboxed iframe that disallows scripts.\n // If so, IMA child iframes may also be sandboxed and block execution.\n try {\n const frameEl = window.frameElement as HTMLIFrameElement | null;\n const sandboxAttr = frameEl?.getAttribute?.(\"sandbox\") || \"\";\n if (sandboxAttr) {\n const tokens = new Set(\n sandboxAttr\n .split(/\\s+/)\n .map((t) => t.trim())\n .filter((t) => t.length > 0)\n );\n const allowsScripts = tokens.has(\"allow-scripts\");\n if (!allowsScripts) {\n // eslint-disable-next-line no-console\n console.error(\n \"StormcloudVideoPlayer: The host page is inside a sandboxed iframe without 'allow-scripts'. Google IMA cannot run ads within sandboxed frames. Remove the sandbox attribute or include 'allow-scripts allow-same-origin'.\"\n );\n }\n }\n } catch {}\n\n if (typeof window !== \"undefined\" && window.google?.ima)\n return Promise.resolve();\n const existing = document.querySelector(\n 'script[data-ima=\"true\"]'\n ) as HTMLScriptElement | null;\n if (existing) {\n return new Promise((resolve) =>\n existing.addEventListener(\"load\", () => resolve())\n );\n }\n return new Promise((resolve, reject) => {\n const script = document.createElement(\"script\");\n script.src = \"https://imasdk.googleapis.com/js/sdkloader/ima3.js\";\n script.async = true;\n script.defer = true;\n script.setAttribute(\"data-ima\", \"true\");\n script.onload = () => resolve();\n script.onerror = () => reject(new Error(\"IMA SDK load failed\"));\n document.head.appendChild(script);\n });\n }\n\n let adsManager: any | undefined;\n let adsLoader: any | undefined;\n let adDisplayContainer: any | undefined;\n let adContainerEl: HTMLDivElement | undefined;\n let lastAdTagUrl: string | undefined;\n let retryAttempts = 0;\n const maxRetries = 2;\n const backoffBaseMs = 500;\n let adsLoadedPromise: Promise<void> | undefined;\n let adsLoadedResolve: (() => void) | undefined;\n let adsLoadedReject: ((error: Error) => void) | undefined;\n\n function makeAdsRequest(google: any, vastTagUrl: string) {\n const adsRequest = new google.ima.AdsRequest();\n adsRequest.adTagUrl = vastTagUrl;\n adsLoader.requestAds(adsRequest);\n }\n\n return {\n initialize() {\n // Lazily load IMA SDK when needed\n ensureImaLoaded()\n .then(() => {\n const google = window.google;\n if (!adDisplayContainer) {\n const container = document.createElement(\"div\");\n container.style.position = \"absolute\";\n container.style.left = \"0\";\n container.style.top = \"0\";\n container.style.right = \"0\";\n container.style.bottom = \"0\";\n container.style.pointerEvents = \"none\";\n container.style.zIndex = \"2\";\n video.parentElement?.appendChild(container);\n adContainerEl = container;\n adDisplayContainer = new google.ima.AdDisplayContainer(\n container,\n video\n );\n try {\n // Some browsers require user gesture before initialize; ignore failures here\n adDisplayContainer.initialize?.();\n } catch {}\n }\n })\n .catch(() => {});\n },\n async requestAds(vastTagUrl: string) {\n console.log(\"[IMA] Requesting ads:\", vastTagUrl);\n\n // Create a new promise for this ad request\n adsLoadedPromise = new Promise<void>((resolve, reject) => {\n adsLoadedResolve = resolve;\n adsLoadedReject = reject;\n\n // Set a timeout to prevent hanging forever\n setTimeout(() => {\n if (adsLoadedReject) {\n adsLoadedReject(new Error(\"Ad request timeout\"));\n adsLoadedReject = undefined;\n adsLoadedResolve = undefined;\n }\n }, 10000); // 10 second timeout\n });\n\n try {\n await ensureImaLoaded();\n const google = window.google;\n lastAdTagUrl = vastTagUrl;\n retryAttempts = 0;\n\n if (!adDisplayContainer) {\n console.log(\"[IMA] Creating ad display container\");\n const container = document.createElement(\"div\");\n container.style.position = \"absolute\";\n container.style.left = \"0\";\n container.style.top = \"0\";\n container.style.right = \"0\";\n container.style.bottom = \"0\";\n container.style.pointerEvents = \"none\";\n container.style.zIndex = \"2\";\n\n if (!video.parentElement) {\n throw new Error(\"Video element has no parent for ad container\");\n }\n\n video.parentElement.appendChild(container);\n adContainerEl = container;\n adDisplayContainer = new google.ima.AdDisplayContainer(\n container,\n video\n );\n\n try {\n adDisplayContainer.initialize();\n console.log(\"[IMA] Ad display container initialized\");\n } catch (error) {\n console.warn(\n \"[IMA] Failed to initialize ad display container:\",\n error\n );\n }\n }\n\n if (!adsLoader) {\n console.log(\"[IMA] Creating ads loader\");\n const adsLoaderCls = new google.ima.AdsLoader(adDisplayContainer);\n adsLoader = adsLoaderCls;\n\n adsLoader.addEventListener(\n google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,\n (evt: any) => {\n console.log(\"[IMA] Ads manager loaded\");\n try {\n adsManager = evt.getAdsManager(video);\n const AdEvent = google.ima.AdEvent.Type;\n const AdErrorEvent = google.ima.AdErrorEvent.Type;\n\n adsManager.addEventListener(\n AdErrorEvent.AD_ERROR,\n (errorEvent: any) => {\n console.error(\"[IMA] Ad error:\", errorEvent.getError());\n try {\n adsManager?.destroy?.();\n } catch {}\n adPlaying = false;\n if (adContainerEl)\n adContainerEl.style.pointerEvents = \"none\";\n\n // Reject the promise if this is the current request\n if (adsLoadedReject) {\n adsLoadedReject(new Error(\"Ad playback error\"));\n adsLoadedReject = undefined;\n adsLoadedResolve = undefined;\n }\n\n // Retry with backoff up to maxRetries\n if (lastAdTagUrl && retryAttempts < maxRetries) {\n const delay =\n backoffBaseMs * Math.pow(2, retryAttempts++);\n console.log(\n `[IMA] Retrying ad request in ${delay}ms (attempt ${retryAttempts})`\n );\n window.setTimeout(() => {\n try {\n makeAdsRequest(google, lastAdTagUrl!);\n } catch {}\n }, delay);\n } else {\n console.log(\n \"[IMA] Max retries reached, emitting ad_error\"\n );\n emit(\"ad_error\");\n video.play().catch(() => {});\n }\n }\n );\n\n adsManager.addEventListener(\n AdEvent.CONTENT_PAUSE_REQUESTED,\n () => {\n console.log(\"[IMA] Content pause requested\");\n video.pause();\n adPlaying = true;\n if (adContainerEl)\n adContainerEl.style.pointerEvents = \"auto\";\n emit(\"content_pause\");\n }\n );\n\n adsManager.addEventListener(\n AdEvent.CONTENT_RESUME_REQUESTED,\n () => {\n console.log(\"[IMA] Content resume requested\");\n adPlaying = false;\n if (adContainerEl)\n adContainerEl.style.pointerEvents = \"none\";\n video.play().catch(() => {});\n emit(\"content_resume\");\n }\n );\n\n adsManager.addEventListener(AdEvent.ALL_ADS_COMPLETED, () => {\n console.log(\"[IMA] All ads completed\");\n adPlaying = false;\n if (adContainerEl) adContainerEl.style.pointerEvents = \"none\";\n video.play().catch(() => {});\n emit(\"all_ads_completed\");\n });\n\n console.log(\"[IMA] Ads manager event listeners attached\");\n\n // Resolve the promise - ads are ready to play\n if (adsLoadedResolve) {\n adsLoadedResolve();\n adsLoadedResolve = undefined;\n adsLoadedReject = undefined;\n }\n } catch (e) {\n console.error(\"[IMA] Error setting up ads manager:\", e);\n adPlaying = false;\n if (adContainerEl) adContainerEl.style.pointerEvents = \"none\";\n video.play().catch(() => {});\n\n if (adsLoadedReject) {\n adsLoadedReject(new Error(\"Failed to setup ads manager\"));\n adsLoadedReject = undefined;\n adsLoadedResolve = undefined;\n }\n emit(\"ad_error\");\n }\n },\n false\n );\n\n adsLoader.addEventListener(\n google.ima.AdErrorEvent.Type.AD_ERROR,\n (adErrorEvent: any) => {\n console.error(\"[IMA] Ads loader error:\", adErrorEvent.getError());\n\n if (adsLoadedReject) {\n adsLoadedReject(new Error(\"Ads loader error\"));\n adsLoadedReject = undefined;\n adsLoadedResolve = undefined;\n }\n emit(\"ad_error\");\n },\n false\n );\n }\n\n console.log(\"[IMA] Making ads request\");\n makeAdsRequest(google, vastTagUrl);\n\n // Return the promise that will resolve when ads are loaded\n return adsLoadedPromise;\n } catch (error) {\n console.error(\"[IMA] Failed to request ads:\", error);\n if (adsLoadedReject) {\n adsLoadedReject(error as Error);\n adsLoadedReject = undefined;\n adsLoadedResolve = undefined;\n }\n return Promise.reject(error);\n }\n },\n async play() {\n if (!window.google?.ima || !adDisplayContainer) {\n console.warn(\n \"[IMA] Cannot play ad: IMA SDK or ad container not available\"\n );\n return Promise.reject(new Error(\"IMA SDK not available\"));\n }\n\n if (!adsManager) {\n console.warn(\"[IMA] Cannot play ad: No ads manager available\");\n return Promise.reject(new Error(\"No ads manager\"));\n }\n\n try {\n const width = video.clientWidth || 640;\n const height = video.clientHeight || 360;\n\n console.log(`[IMA] Initializing ads manager (${width}x${height})`);\n adsManager.init(width, height, window.google.ima.ViewMode.NORMAL);\n\n console.log(\"[IMA] Pausing video for ad playback\");\n video.pause();\n adPlaying = true;\n\n console.log(\"[IMA] Starting ad playback\");\n adsManager.start();\n\n return Promise.resolve();\n } catch (error) {\n console.error(\"[IMA] Error starting ad playback:\", error);\n adPlaying = false;\n video.play().catch(() => {});\n return Promise.reject(error);\n }\n },\n async stop() {\n adPlaying = false;\n try {\n adsManager?.stop?.();\n } catch {}\n video.play().catch(() => {});\n },\n destroy() {\n try {\n adsManager?.destroy?.();\n } catch {}\n adPlaying = false;\n try {\n adsLoader?.destroy?.();\n } catch {}\n if (adContainerEl?.parentElement) {\n adContainerEl.parentElement.removeChild(adContainerEl);\n }\n },\n isAdPlaying() {\n return adPlaying;\n },\n on(event: string, listener: (payload?: any) => void) {\n if (!listeners.has(event)) listeners.set(event, new Set());\n listeners.get(event)!.add(listener);\n },\n off(event: string, listener: (payload?: any) => void) {\n listeners.get(event)?.delete(listener);\n },\n };\n}\n","import type { ClientInfo, TrackingData, HeartbeatData } from \"../types\";\n\nexport function getClientInfo(): ClientInfo {\n const ua = navigator.userAgent;\n const platform = navigator.platform;\n const vendor = navigator.vendor || \"\";\n const maxTouchPoints = navigator.maxTouchPoints || 0;\n const memory = (navigator as any).deviceMemory || null; // MB (only in modern browsers)\n const hardwareConcurrency = navigator.hardwareConcurrency || 1;\n\n // Screen info\n const screenInfo = {\n width: screen?.width,\n height: screen?.height,\n availWidth: screen?.availWidth,\n availHeight: screen?.availHeight,\n orientation: (screen?.orientation as any)?.type || \"\",\n pixelDepth: screen?.pixelDepth,\n };\n\n // Detect environment\n let deviceType: \"tv\" | \"mobile\" | \"tablet\" | \"desktop\" = \"desktop\";\n let brand = \"Unknown\";\n let os = \"Unknown\";\n let model = \"\";\n let isSmartTV = false;\n let isAndroid = false;\n let isWebView = false;\n let isWebApp = false;\n\n // --- Detect Smart TVs ---\n if (ua.includes(\"Web0S\")) {\n brand = \"LG\";\n os = \"webOS\";\n isSmartTV = true;\n deviceType = \"tv\";\n // Extract model if possible (LG uses specific identifiers)\n const webosMatch = ua.match(/Web0S\\/([^\\s]+)/);\n model = webosMatch ? `webOS ${webosMatch[1]}` : \"webOS TV\";\n } else if (ua.includes(\"Tizen\")) {\n brand = \"Samsung\";\n os = \"Tizen\";\n isSmartTV = true;\n deviceType = \"tv\";\n const tizenMatch = ua.match(/Tizen\\/([^\\s]+)/);\n const tvMatch = ua.match(/(?:Smart-TV|SMART-TV|TV)/i) ? \"Smart TV\" : \"\";\n model = tizenMatch\n ? `Tizen ${tizenMatch[1]} ${tvMatch}`.trim()\n : \"Tizen TV\";\n } else if (ua.includes(\"Philips\")) {\n brand = \"Philips\";\n os = \"Saphi\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (ua.includes(\"Sharp\") || ua.includes(\"AQUOS\")) {\n brand = \"Sharp\";\n os = \"Android TV\"; // Most Sharp TVs are Android-based\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (\n ua.includes(\"Android\") &&\n (ua.includes(\"Sony\") || vendor.includes(\"Sony\"))\n ) {\n brand = \"Sony\";\n os = \"Android TV\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (\n ua.includes(\"Android\") &&\n (ua.includes(\"NetCast\") || ua.includes(\"LG\"))\n ) {\n brand = \"LG\";\n os = \"Android TV\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (ua.includes(\" Roku\") || ua.includes(\"Roku/\")) {\n brand = \"Roku\";\n os = \"Roku OS\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (ua.includes(\"AppleTV\")) {\n brand = \"Apple\";\n os = \"tvOS\";\n isSmartTV = true;\n deviceType = \"tv\";\n }\n\n // --- Detect Android ---\n if (ua.includes(\"Android\")) {\n isAndroid = true;\n os = \"Android\";\n deviceType = /Mobile/.test(ua) ? \"mobile\" : \"tablet\";\n\n // Detect Android TV\n if (\n ua.includes(\"Android\") &&\n (maxTouchPoints === 0 ||\n ua.includes(\"Google TV\") ||\n ua.includes(\"XiaoMi\"))\n ) {\n deviceType = \"tv\";\n isSmartTV = true;\n brand = brand === \"Unknown\" ? \"Android TV\" : brand;\n }\n\n // Try to extract Android model\n const androidModelMatch = ua.match(/\\(([^)]*Android[^)]*)\\)/);\n if (androidModelMatch && androidModelMatch[1]) {\n model = androidModelMatch[1];\n }\n }\n\n // --- Detect iOS ---\n if (/iPad|iPhone|iPod/.test(ua)) {\n os = \"iOS\";\n deviceType = \"mobile\";\n brand = \"Apple\";\n // Detect iPadOS as tablet\n if (navigator.maxTouchPoints > 1 && /iPad/.test(ua)) {\n deviceType = \"tablet\";\n }\n }\n\n // --- Detect Desktop OS ---\n if (!isAndroid && !isSmartTV && !/Mobile/.test(ua)) {\n if (ua.includes(\"Windows\")) {\n os = \"Windows\";\n deviceType = \"desktop\";\n } else if (ua.includes(\"Mac\") && !/iPhone/.test(ua)) {\n os = \"macOS\";\n deviceType = \"desktop\";\n if (maxTouchPoints > 1) deviceType = \"tablet\"; // iPad with macOS UA\n } else if (ua.includes(\"Linux\")) {\n os = \"Linux\";\n deviceType = \"desktop\";\n }\n }\n\n // --- Detect Brand (fallback) ---\n if (brand === \"Unknown\") {\n if (vendor.includes(\"Google\") || ua.includes(\"Chrome\")) brand = \"Google\";\n if (vendor.includes(\"Apple\")) brand = \"Apple\";\n if (vendor.includes(\"Samsung\") || ua.includes(\"SM-\")) brand = \"Samsung\";\n }\n\n // --- Detect WebView ---\n // Common signs: no window controls, missing menus, specific UA patterns\n isWebView = /wv|WebView|Linux; U;/.test(ua);\n\n // More reliable detection (Android WebViews often lack certain APIs)\n if (window?.outerHeight === 0 && window?.outerWidth === 0) {\n isWebView = true;\n }\n\n // Standalone PWA or full app?\n isWebApp =\n window.matchMedia(\"(display-mode: standalone)\").matches ||\n (window.navigator as any).standalone === true ||\n window.screen?.orientation?.angle !== undefined;\n\n return {\n // Identity\n brand,\n os,\n model: model || ua.substring(0, 50) + \"...\",\n deviceType,\n isSmartTV,\n isAndroid,\n isWebView,\n isWebApp,\n\n // Environment\n domain: window.location.hostname,\n origin: window.location.origin,\n path: window.location.pathname,\n userAgent: ua,\n vendor,\n platform,\n\n // Hardware\n screen: screenInfo,\n hardwareConcurrency,\n deviceMemory: memory,\n maxTouchPoints,\n\n language: navigator.language,\n languages: navigator.languages?.join(\",\") || \"\",\n cookieEnabled: navigator.cookieEnabled,\n doNotTrack: navigator.doNotTrack || \"\",\n\n // Context\n referrer: document.referrer,\n visibilityState: document.visibilityState,\n };\n}\n\nexport async function getBrowserID(clientInfo: ClientInfo): Promise<string> {\n const fingerprintString = JSON.stringify(clientInfo);\n const hashBuffer = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(fingerprintString)\n );\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hashHex = hashArray\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n return hashHex;\n}\n\nexport async function sendInitialTracking(): Promise<void> {\n try {\n const clientInfo = getClientInfo();\n const browserId = await getBrowserID(clientInfo);\n\n const trackingData: TrackingData = {\n browserId,\n ...clientInfo,\n };\n\n const response = await fetch(\n \"https://stormcloud.co/api-stormcloud-dev/stormcloud/player-tracking/track\",\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(trackingData),\n }\n );\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const data = await response.json();\n console.log(\"[StormcloudVideoPlayer] Initial tracking data sent successfully:\", data);\n } catch (error) {\n console.error(\"[StormcloudVideoPlayer] Error sending initial tracking data:\", error);\n }\n}\n\nexport async function sendHeartbeat(): Promise<void> {\n try {\n const clientInfo = getClientInfo();\n const browserId = await getBrowserID(clientInfo);\n\n const heartbeatData: HeartbeatData = {\n browserId,\n timestamp: new Date().toISOString(),\n };\n\n const response = await fetch(\n \"https://stormcloud.co/api-stormcloud-dev/stormcloud/player-tracking/heartbeat\",\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(heartbeatData),\n }\n );\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const data = await response.json();\n console.log(\"[StormcloudVideoPlayer] Heartbeat sent successfully:\", data);\n } catch (error) {\n console.error(\"[StormcloudVideoPlayer] Error sending heartbeat:\", error);\n }\n}\n","import React, { useEffect, useRef } from \"react\";\nimport { StormcloudVideoPlayer } from \"../player/StormcloudVideoPlayer\";\nimport type { StormcloudVideoPlayerConfig } from \"../types\";\n\nexport type StormcloudVideoPlayerProps = Omit<StormcloudVideoPlayerConfig, \"videoElement\"> &\n React.VideoHTMLAttributes<HTMLVideoElement> & {\n onReady?: (player: StormcloudVideoPlayer) => void;\n wrapperClassName?: string;\n wrapperStyle?: React.CSSProperties;\n };\n\nexport const StormcloudVideoPlayerComponent: React.FC<StormcloudVideoPlayerProps> = (props) => {\n const {\n // Player config props\n src,\n autoplay,\n muted,\n lowLatencyMode,\n allowNativeHls,\n driftToleranceMs,\n adSchedule,\n immediateManifestAds,\n debugAdTiming,\n showCustomControls,\n onVolumeToggle,\n onFullscreenToggle,\n onControlClick,\n\n // UI / DOM props\n onReady,\n wrapperClassName,\n wrapperStyle,\n\n // Video element props (filtered)\n className,\n style,\n controls,\n playsInline,\n preload,\n poster,\n // Intentionally exclude src/autoplay/muted from spread to avoid conflicts\n children,\n ...restVideoAttrs\n } = props;\n\n const videoRef = useRef<HTMLVideoElement | null>(null);\n const playerRef = useRef<StormcloudVideoPlayer | null>(null);\n const [adStatus, setAdStatus] = React.useState<{\n showAds: boolean;\n currentIndex: number;\n totalAds: number;\n }>({ showAds: false, currentIndex: 0, totalAds: 0 });\n\n const [shouldShowNativeControls, setShouldShowNativeControls] =\n React.useState(true);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return; // SSR safety\n const el = videoRef.current;\n if (!el || !src) return;\n\n // Destroy any previous instance\n if (playerRef.current) {\n try {\n playerRef.current.destroy();\n } catch {}\n playerRef.current = null;\n }\n\n const cfg: StormcloudVideoPlayerConfig = {\n src,\n videoElement: el,\n } as StormcloudVideoPlayerConfig;\n if (autoplay !== undefined) cfg.autoplay = autoplay;\n if (muted !== undefined) cfg.muted = muted;\n if (lowLatencyMode !== undefined) cfg.lowLatencyMode = lowLatencyMode;\n if (allowNativeHls !== undefined) cfg.allowNativeHls = allowNativeHls;\n if (driftToleranceMs !== undefined) cfg.driftToleranceMs = driftToleranceMs;\n if (adSchedule !== undefined) cfg.adSchedule = adSchedule;\n if (immediateManifestAds !== undefined)\n cfg.immediateManifestAds = immediateManifestAds;\n if (debugAdTiming !== undefined) cfg.debugAdTiming = debugAdTiming;\n if (showCustomControls !== undefined)\n cfg.showCustomControls = showCustomControls;\n if (onVolumeToggle !== undefined) cfg.onVolumeToggle = onVolumeToggle;\n if (onFullscreenToggle !== undefined)\n cfg.onFullscreenToggle = onFullscreenToggle;\n if (onControlClick !== undefined) cfg.onControlClick = onControlClick;\n\n const player = new StormcloudVideoPlayer(cfg);\n playerRef.current = player;\n player\n .load()\n .then(() => {\n // Update controls settings based on stream type\n const showNative = player.shouldShowNativeControls();\n setShouldShowNativeControls(showNative);\n onReady?.(player);\n })\n .catch(() => {});\n\n return () => {\n try {\n player.destroy();\n } catch {}\n playerRef.current = null;\n };\n }, [\n src,\n autoplay,\n muted,\n lowLatencyMode,\n allowNativeHls,\n driftToleranceMs,\n immediateManifestAds,\n debugAdTiming,\n showCustomControls,\n onVolumeToggle,\n onFullscreenToggle,\n onControlClick,\n onReady,\n ]);\n\n // Reflect adSchedule updates without full re-instantiation when only schedule changes\n useEffect(() => {\n if (!playerRef.current) return;\n if (adSchedule) {\n try {\n playerRef.current.setAdSchedule(adSchedule);\n } catch {}\n }\n }, [adSchedule]);\n\n // Monitor ad status for counter display\n useEffect(() => {\n if (!playerRef.current) return;\n\n const checkAdStatus = () => {\n if (playerRef.current) {\n const showAds = playerRef.current.isShowingAds();\n const currentIndex = playerRef.current.getCurrentAdIndex();\n const totalAds = playerRef.current.getTotalAdsInBreak();\n\n setAdStatus((prev) => {\n if (\n prev.showAds !== showAds ||\n prev.currentIndex !== currentIndex ||\n prev.totalAds !== totalAds\n ) {\n return { showAds, currentIndex, totalAds };\n }\n return prev;\n });\n }\n };\n\n const interval = setInterval(checkAdStatus, 500); // Check every 500ms\n return () => clearInterval(interval);\n }, []);\n\n return (\n <div\n className={wrapperClassName}\n style={{ position: \"relative\", overflow: \"hidden\", ...wrapperStyle }}\n >\n <video\n ref={videoRef}\n className={className}\n style={{ display: \"block\", width: \"100%\", height: \"100%\", ...style }}\n controls={shouldShowNativeControls && controls}\n playsInline={playsInline}\n preload={preload}\n poster={poster}\n {...restVideoAttrs}\n >\n {children}\n </video>\n\n {/* Ad Counter Display */}\n {adStatus.showAds && adStatus.totalAds > 0 && (\n <div\n style={{\n position: \"absolute\",\n top: \"10px\",\n right: \"10px\",\n backgroundColor: \"rgba(0, 0, 0, 0.7)\",\n color: \"white\",\n padding: \"4px 8px\",\n borderRadius: \"4px\",\n fontSize: \"12px\",\n fontFamily: \"Arial, sans-serif\",\n zIndex: 10,\n }}\n >\n Ad {adStatus.currentIndex}/{adStatus.totalAds}\n </div>\n )}\n\n {/* Custom Controls Overlay for HLS streams */}\n {!shouldShowNativeControls && showCustomControls && (\n <div\n style={{\n position: \"absolute\",\n bottom: \"10px\",\n right: \"10px\",\n display: \"flex\",\n gap: \"8px\",\n zIndex: 10,\n }}\n >\n {onVolumeToggle && (\n <button\n onClick={onVolumeToggle}\n style={{\n backgroundColor: \"rgba(0, 0, 0, 0.7)\",\n color: \"white\",\n border: \"1px solid rgba(255, 255, 255, 0.3)\",\n borderRadius: \"4px\",\n padding: \"8px\",\n cursor: \"pointer\",\n fontSize: \"14px\",\n }}\n title=\"Toggle Volume\"\n >\n 🔊\n </button>\n )}\n\n {onFullscreenToggle && (\n <button\n onClick={onFullscreenToggle}\n style={{\n backgroundColor: \"rgba(0, 0, 0, 0.7)\",\n color: \"white\",\n border: \"1px solid rgba(255, 255, 255, 0.3)\",\n borderRadius: \"4px\",\n padding: \"8px\",\n cursor: \"pointer\",\n fontSize: \"14px\",\n }}\n title=\"Toggle Fullscreen\"\n >\n ⤢\n </button>\n )}\n </div>\n )}\n\n {/* Click overlay for control interactions */}\n {onControlClick && (\n <div\n onClick={onControlClick}\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n zIndex: 1,\n cursor: \"pointer\",\n }}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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","names":["Hls","React"]}
@@ -0,0 +1,157 @@
1
+ import React from 'react';
2
+
3
+ type LateJoinPolicy = "play_remaining" | "skip_to_content";
4
+ interface AdBreak {
5
+ id?: string;
6
+ startTimeMs: number;
7
+ durationMs?: number;
8
+ vastTagUrl?: string;
9
+ }
10
+ interface AdSchedule {
11
+ breaks: AdBreak[];
12
+ lateJoinPolicy?: LateJoinPolicy;
13
+ }
14
+ interface StormcloudVideoPlayerConfig {
15
+ videoElement: HTMLVideoElement;
16
+ src: string;
17
+ autoplay?: boolean;
18
+ muted?: boolean;
19
+ allowNativeHls?: boolean;
20
+ adSchedule?: AdSchedule;
21
+ lowLatencyMode?: boolean;
22
+ driftToleranceMs?: number;
23
+ /** Force immediate ad start for all manifest-based markers (EXT-X-CUE-OUT, etc.) */
24
+ immediateManifestAds?: boolean;
25
+ /** Enable debug logging for ad insertion timing */
26
+ debugAdTiming?: boolean;
27
+ /** Failsafe timeout in milliseconds to resume video if ad fails (default: 10000) */
28
+ adFailsafeTimeoutMs?: number;
29
+ /** Show custom controls overlay */
30
+ showCustomControls?: boolean;
31
+ /** Custom control callbacks */
32
+ onVolumeToggle?: () => void;
33
+ onFullscreenToggle?: () => void;
34
+ onControlClick?: () => void;
35
+ }
36
+ interface ClientInfo {
37
+ brand: string;
38
+ os: string;
39
+ model: string;
40
+ deviceType: "tv" | "mobile" | "tablet" | "desktop";
41
+ isSmartTV: boolean;
42
+ isAndroid: boolean;
43
+ isWebView: boolean;
44
+ isWebApp: boolean;
45
+ domain: string;
46
+ origin: string;
47
+ path: string;
48
+ userAgent: string;
49
+ vendor: string;
50
+ platform: string;
51
+ screen: {
52
+ width?: number;
53
+ height?: number;
54
+ availWidth?: number;
55
+ availHeight?: number;
56
+ orientation?: string;
57
+ pixelDepth?: number;
58
+ };
59
+ hardwareConcurrency: number;
60
+ deviceMemory: number | null;
61
+ maxTouchPoints: number;
62
+ language: string;
63
+ languages: string;
64
+ cookieEnabled: boolean;
65
+ doNotTrack: string;
66
+ referrer: string;
67
+ visibilityState: string;
68
+ }
69
+ interface TrackingData extends ClientInfo {
70
+ browserId: string;
71
+ }
72
+ interface HeartbeatData {
73
+ browserId: string;
74
+ timestamp: string;
75
+ }
76
+
77
+ declare class StormcloudVideoPlayer {
78
+ private readonly video;
79
+ private readonly config;
80
+ private hls?;
81
+ private ima;
82
+ private attached;
83
+ private adSchedule;
84
+ private inAdBreak;
85
+ private currentAdBreakStartWallClockMs;
86
+ private expectedAdBreakDurationMs;
87
+ private adStopTimerId;
88
+ private adStartTimerId;
89
+ private adFailsafeTimerId;
90
+ private ptsDriftEmaMs;
91
+ private adPodQueue;
92
+ private apiVastTagUrl;
93
+ private vastConfig;
94
+ private lastHeartbeatTime;
95
+ private heartbeatInterval;
96
+ private currentAdIndex;
97
+ private totalAdsInBreak;
98
+ private showAds;
99
+ constructor(config: StormcloudVideoPlayerConfig);
100
+ load(): Promise<void>;
101
+ private attach;
102
+ private shouldUseNativeHls;
103
+ private onId3Tag;
104
+ private parseScte35FromId3;
105
+ private decodeId3ValueToText;
106
+ private onScte35Marker;
107
+ private parseCueOutDuration;
108
+ private parseCueOutCont;
109
+ private parseAttributeList;
110
+ private toNumber;
111
+ private isManifestBasedMarker;
112
+ private parseScte35Binary;
113
+ private initializeTracking;
114
+ private sendHeartbeatIfNeeded;
115
+ private fetchAdConfiguration;
116
+ setAdSchedule(schedule: AdSchedule | undefined): void;
117
+ getCurrentAdIndex(): number;
118
+ getTotalAdsInBreak(): number;
119
+ isAdPlaying(): boolean;
120
+ isShowingAds(): boolean;
121
+ getStreamType(): "hls" | "other";
122
+ shouldShowNativeControls(): boolean;
123
+ loadAdScheduleFromUrl(url: string): Promise<void>;
124
+ loadDefaultVastFromAiry(airyApiUrl: string, params?: Record<string, string>): Promise<void>;
125
+ private handleAdStart;
126
+ private findCurrentOrNextBreak;
127
+ private onTimeUpdate;
128
+ private handleMidAdJoin;
129
+ private scheduleAdStopCountdown;
130
+ private clearAdStopTimer;
131
+ private ensureAdStoppedByTimer;
132
+ private scheduleAdStartIn;
133
+ private clearAdStartTimer;
134
+ private updatePtsDrift;
135
+ private playSingleAd;
136
+ private handleAdFailure;
137
+ private startAdFailsafeTimer;
138
+ private clearAdFailsafeTimer;
139
+ private selectVastTagsForBreak;
140
+ private getRemainingAdMs;
141
+ private findBreakForTime;
142
+ destroy(): void;
143
+ }
144
+
145
+ type StormcloudVideoPlayerProps = Omit<StormcloudVideoPlayerConfig, "videoElement"> & React.VideoHTMLAttributes<HTMLVideoElement> & {
146
+ onReady?: (player: StormcloudVideoPlayer) => void;
147
+ wrapperClassName?: string;
148
+ wrapperStyle?: React.CSSProperties;
149
+ };
150
+ declare const StormcloudVideoPlayerComponent: React.FC<StormcloudVideoPlayerProps>;
151
+
152
+ declare function getClientInfo(): ClientInfo;
153
+ declare function getBrowserID(clientInfo: ClientInfo): Promise<string>;
154
+ declare function sendInitialTracking(): Promise<void>;
155
+ declare function sendHeartbeat(): Promise<void>;
156
+
157
+ export { type AdBreak, type AdSchedule, type ClientInfo, type HeartbeatData, type LateJoinPolicy, StormcloudVideoPlayer, StormcloudVideoPlayerComponent, type StormcloudVideoPlayerConfig, type StormcloudVideoPlayerProps, type TrackingData, getBrowserID, getClientInfo, sendHeartbeat, sendInitialTracking };
package/lib/index.d.ts ADDED
@@ -0,0 +1,157 @@
1
+ import React from 'react';
2
+
3
+ type LateJoinPolicy = "play_remaining" | "skip_to_content";
4
+ interface AdBreak {
5
+ id?: string;
6
+ startTimeMs: number;
7
+ durationMs?: number;
8
+ vastTagUrl?: string;
9
+ }
10
+ interface AdSchedule {
11
+ breaks: AdBreak[];
12
+ lateJoinPolicy?: LateJoinPolicy;
13
+ }
14
+ interface StormcloudVideoPlayerConfig {
15
+ videoElement: HTMLVideoElement;
16
+ src: string;
17
+ autoplay?: boolean;
18
+ muted?: boolean;
19
+ allowNativeHls?: boolean;
20
+ adSchedule?: AdSchedule;
21
+ lowLatencyMode?: boolean;
22
+ driftToleranceMs?: number;
23
+ /** Force immediate ad start for all manifest-based markers (EXT-X-CUE-OUT, etc.) */
24
+ immediateManifestAds?: boolean;
25
+ /** Enable debug logging for ad insertion timing */
26
+ debugAdTiming?: boolean;
27
+ /** Failsafe timeout in milliseconds to resume video if ad fails (default: 10000) */
28
+ adFailsafeTimeoutMs?: number;
29
+ /** Show custom controls overlay */
30
+ showCustomControls?: boolean;
31
+ /** Custom control callbacks */
32
+ onVolumeToggle?: () => void;
33
+ onFullscreenToggle?: () => void;
34
+ onControlClick?: () => void;
35
+ }
36
+ interface ClientInfo {
37
+ brand: string;
38
+ os: string;
39
+ model: string;
40
+ deviceType: "tv" | "mobile" | "tablet" | "desktop";
41
+ isSmartTV: boolean;
42
+ isAndroid: boolean;
43
+ isWebView: boolean;
44
+ isWebApp: boolean;
45
+ domain: string;
46
+ origin: string;
47
+ path: string;
48
+ userAgent: string;
49
+ vendor: string;
50
+ platform: string;
51
+ screen: {
52
+ width?: number;
53
+ height?: number;
54
+ availWidth?: number;
55
+ availHeight?: number;
56
+ orientation?: string;
57
+ pixelDepth?: number;
58
+ };
59
+ hardwareConcurrency: number;
60
+ deviceMemory: number | null;
61
+ maxTouchPoints: number;
62
+ language: string;
63
+ languages: string;
64
+ cookieEnabled: boolean;
65
+ doNotTrack: string;
66
+ referrer: string;
67
+ visibilityState: string;
68
+ }
69
+ interface TrackingData extends ClientInfo {
70
+ browserId: string;
71
+ }
72
+ interface HeartbeatData {
73
+ browserId: string;
74
+ timestamp: string;
75
+ }
76
+
77
+ declare class StormcloudVideoPlayer {
78
+ private readonly video;
79
+ private readonly config;
80
+ private hls?;
81
+ private ima;
82
+ private attached;
83
+ private adSchedule;
84
+ private inAdBreak;
85
+ private currentAdBreakStartWallClockMs;
86
+ private expectedAdBreakDurationMs;
87
+ private adStopTimerId;
88
+ private adStartTimerId;
89
+ private adFailsafeTimerId;
90
+ private ptsDriftEmaMs;
91
+ private adPodQueue;
92
+ private apiVastTagUrl;
93
+ private vastConfig;
94
+ private lastHeartbeatTime;
95
+ private heartbeatInterval;
96
+ private currentAdIndex;
97
+ private totalAdsInBreak;
98
+ private showAds;
99
+ constructor(config: StormcloudVideoPlayerConfig);
100
+ load(): Promise<void>;
101
+ private attach;
102
+ private shouldUseNativeHls;
103
+ private onId3Tag;
104
+ private parseScte35FromId3;
105
+ private decodeId3ValueToText;
106
+ private onScte35Marker;
107
+ private parseCueOutDuration;
108
+ private parseCueOutCont;
109
+ private parseAttributeList;
110
+ private toNumber;
111
+ private isManifestBasedMarker;
112
+ private parseScte35Binary;
113
+ private initializeTracking;
114
+ private sendHeartbeatIfNeeded;
115
+ private fetchAdConfiguration;
116
+ setAdSchedule(schedule: AdSchedule | undefined): void;
117
+ getCurrentAdIndex(): number;
118
+ getTotalAdsInBreak(): number;
119
+ isAdPlaying(): boolean;
120
+ isShowingAds(): boolean;
121
+ getStreamType(): "hls" | "other";
122
+ shouldShowNativeControls(): boolean;
123
+ loadAdScheduleFromUrl(url: string): Promise<void>;
124
+ loadDefaultVastFromAiry(airyApiUrl: string, params?: Record<string, string>): Promise<void>;
125
+ private handleAdStart;
126
+ private findCurrentOrNextBreak;
127
+ private onTimeUpdate;
128
+ private handleMidAdJoin;
129
+ private scheduleAdStopCountdown;
130
+ private clearAdStopTimer;
131
+ private ensureAdStoppedByTimer;
132
+ private scheduleAdStartIn;
133
+ private clearAdStartTimer;
134
+ private updatePtsDrift;
135
+ private playSingleAd;
136
+ private handleAdFailure;
137
+ private startAdFailsafeTimer;
138
+ private clearAdFailsafeTimer;
139
+ private selectVastTagsForBreak;
140
+ private getRemainingAdMs;
141
+ private findBreakForTime;
142
+ destroy(): void;
143
+ }
144
+
145
+ type StormcloudVideoPlayerProps = Omit<StormcloudVideoPlayerConfig, "videoElement"> & React.VideoHTMLAttributes<HTMLVideoElement> & {
146
+ onReady?: (player: StormcloudVideoPlayer) => void;
147
+ wrapperClassName?: string;
148
+ wrapperStyle?: React.CSSProperties;
149
+ };
150
+ declare const StormcloudVideoPlayerComponent: React.FC<StormcloudVideoPlayerProps>;
151
+
152
+ declare function getClientInfo(): ClientInfo;
153
+ declare function getBrowserID(clientInfo: ClientInfo): Promise<string>;
154
+ declare function sendInitialTracking(): Promise<void>;
155
+ declare function sendHeartbeat(): Promise<void>;
156
+
157
+ export { type AdBreak, type AdSchedule, type ClientInfo, type HeartbeatData, type LateJoinPolicy, StormcloudVideoPlayer, StormcloudVideoPlayerComponent, type StormcloudVideoPlayerConfig, type StormcloudVideoPlayerProps, type TrackingData, getBrowserID, getClientInfo, sendHeartbeat, sendInitialTracking };