@ngxsplayer/ngx-smart-player 0.0.1-next.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,"file":"ngxsplayer-ngx-smart-player.mjs","sources":["../../../projects/ngx-smart-player/src/lib/smart-player.component.ts","../../../projects/ngx-smart-player/src/lib/smart-player.providers.ts","../../../projects/ngx-smart-player/src/ngxsplayer-ngx-smart-player.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Subscription } from 'rxjs';\n\nimport { MediaTrack, PlayerConfig, PlayerState, PlayerPorts, createSsaiManifestRewritePlugin, type EngineFactory } from '@ngxsp/core';\nimport { PlayerFacade } from '@ngxsp/core';\nimport { PlayerPlugin } from '@ngxsp/core';\n\n@Component({\n selector: 'ngx-smart-player',\n standalone: true,\n imports: [CommonModule],\n template: `\n <div class=\"ngxsp-root\" [class.ngxsp-skin-netflix]=\"uiSkin==='netflix'\" [class.ngxsp-ui-visible]=\"uiVisible\" [attr.data-kind]=\"state?.kind\">\n <div class=\"ngxsp-video\" (dblclick)=\"toggleFullscreen()\" (mousemove)=\"onInteraction()\" (mouseenter)=\"onInteraction()\" (mouseleave)=\"onPointerLeave()\" (focusin)=\"onInteraction()\" (touchstart)=\"onInteraction(true)\" (click)=\"onRootClick($event)\">\n <video #video playsinline (timeupdate)=\"noop()\" (loadedmetadata)=\"onLoadedMetadata()\" (ended)=\"onEnded()\"></video>\n@if (state?.kind==='error') {\n<div class=\"ngxsp-overlay\" role=\"alert\">\n <div class=\"ngxsp-overlay-card\">\n <div class=\"ngxsp-overlay-title\">Playback error</div>\n <div class=\"ngxsp-overlay-msg\">\n <div style=\"font-weight:600; margin-bottom:6px;\">{{errorTitle()}}</div>\n <div style=\"opacity:.85; font-size: 13px; line-height: 1.3;\">{{errorHint()}}</div>\n <div style=\"margin-top:10px; display:flex; gap:8px; flex-wrap:wrap;\">\n\t <button type=\"button\" class=\"ngxsp-btn\" (click)=\"reload()\">Retry</button>\n @if (autoplayBlocked) {\n\t <button type=\"button\" class=\"ngxsp-btn\" (click)=\"tryPlay()\">Play</button>\n }\n </div>\n </div>\n </div>\n</div>\n}\n\n@if (debug) {\n<details class=\"ngxsp-debug\" (click)=\"$event.stopPropagation()\">\n <summary>Debug</summary>\n\t <div style=\"display:flex; gap:8px; margin:8px 0; flex-wrap:wrap;\">\n\t <button type=\"button\" class=\"ngxsp-btn\" (click)=\"copyDebug()\">Copy JSON</button>\n\t <button type=\"button\" class=\"ngxsp-btn\" (click)=\"downloadDebug()\">Download</button>\n\t </div>\n <pre>{{debugJson}}</pre>\n</details>\n}\n\n @if (uiSkin==='netflix') {\n <div class=\"ngxsp-nfx-top\">\n <div class=\"ngxsp-nfx-titleWrap\">\n <div class=\"ngxsp-nfx-eyebrow\">Now Playing</div>\n <div class=\"ngxsp-nfx-title\">{{displayTitle}}</div>\n </div>\n <div class=\"ngxsp-nfx-spacer\"></div>\n <div class=\"ngxsp-nfx-status\">{{ statusLabel() }}</div>\n <div class=\"ngxsp-nfx-topHint\">Double-click for fullscreen</div>\n </div>\n\n <div class=\"ngxsp-nfx-centerDock\">\n @if (hasPlaylistControls) {\n <button type=\"button\" class=\"ngxsp-nfx-side\" (click)=\"emitPrev()\" [disabled]=\"!canPrev\" aria-label=\"Previous item\">\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M9.53 12 18 18.47v-12L9.53 12Zm-1.53 6.25a.75.75 0 0 0 1.25-.56V6.31a.75.75 0 0 0-1.25-.56L1.06 11.44a.75.75 0 0 0 0 1.12L8 18.25Z\"/></svg>\n </button>\n }\n <button type=\"button\" class=\"ngxsp-nfx-side ngxsp-nfx-side-wide\" (click)=\"seekRel(-10)\" aria-label=\"Seek backward 10 seconds\">\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M9 5H5m0 0v4m0-4 4.5 4.5A7 7 0 1 1 5 12\"/></svg>\n <span class=\"ngxsp-iconBadge\">10</span>\n </button>\n <button type=\"button\" class=\"ngxsp-nfx-center\" (click)=\"togglePlay()\" aria-label=\"Play/Pause\">\n @if (state?.kind==='playing') {\n <svg class=\"ngxsp-icon ngxsp-icon-lg\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M7.5 5.25A1.25 1.25 0 0 1 8.75 6.5v11A1.25 1.25 0 0 1 6.25 17.5v-11A1.25 1.25 0 0 1 7.5 5.25Zm8 0a1.25 1.25 0 0 1 1.25 1.25v11a1.25 1.25 0 1 1-2.5 0v-11A1.25 1.25 0 0 1 15.5 5.25Z\"/></svg>\n } @else {\n <svg class=\"ngxsp-icon ngxsp-icon-lg\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M8.25 6.82c0-1.3 1.43-2.1 2.54-1.43l8.12 4.93a1.96 1.96 0 0 1 0 3.36l-8.12 4.93a1.67 1.67 0 0 1-2.54-1.43V6.82Z\"/></svg>\n }\n </button>\n <button type=\"button\" class=\"ngxsp-nfx-side ngxsp-nfx-side-wide\" (click)=\"seekRel(10)\" aria-label=\"Seek forward 10 seconds\">\n <span class=\"ngxsp-iconBadge\">10</span>\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M15 5h4m0 0v4m0-4-4.5 4.5A7 7 0 1 0 19 12\"/></svg>\n </button>\n @if (hasPlaylistControls) {\n <button type=\"button\" class=\"ngxsp-nfx-side\" (click)=\"emitNext()\" [disabled]=\"!canNext\" aria-label=\"Next item\">\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M14.47 12 6 5.53v12L14.47 12ZM16 5.75a.75.75 0 0 0-1.25.56v11.38a.75.75 0 0 0 1.25.56l6.94-5.69a.75.75 0 0 0 0-1.12L16 5.75Z\"/></svg>\n </button>\n }\n </div>\n\n <div class=\"ngxsp-controls ngxsp-nfx-bottom\">\n <div class=\"ngxsp-nfx-metaRow\">\n <div class=\"ngxsp-nfx-time\">{{fmtTime(currentTime)}} / {{fmtTime(duration)}}</div>\n <div class=\"ngxsp-nfx-volumeGroup\">\n <button type=\"button\" class=\"ngxsp-nfx-chip ngxsp-nfx-chip-quiet\" (click)=\"toggleMute()\" [attr.title]=\"volumeLabel()\" [attr.aria-label]=\"volumeLabel()\">\n @if (volume <= 0.001) {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M11 5 6.5 9H3.75v6h2.75L11 19V5Zm3.5 5.5 5 5m0-5-5 5\"/></svg>\n } @else if (volume < 0.5) {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M11 5 6.5 9H3.75v6h2.75L11 19V5Zm4.5 4.75a3.5 3.5 0 0 1 0 4.95\"/></svg>\n } @else {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M11 5 6.5 9H3.75v6h2.75L11 19V5Zm4.5 4.75a3.5 3.5 0 0 1 0 4.95m2.75-7.7a7.5 7.5 0 0 1 0 10.45\"/></svg>\n }\n </button>\n <input class=\"ngxsp-range ngxsp-nfx-volume\" type=\"range\"\n min=\"0\" max=\"1\" step=\"0.01\"\n [value]=\"volume\"\n (input)=\"onVolume(($any($event.target).value))\"\n aria-label=\"Volume\"\n />\n </div>\n <div class=\"ngxsp-nfx-spacer\"></div>\n @if (hasPlaylistControls) {\n <button type=\"button\" class=\"ngxsp-nfx-chip\" [class.accent]=\"shuffleActive\" (click)=\"toggleShuffle.emit()\" [attr.title]=\"shuffleLabel\" [attr.aria-label]=\"shuffleLabel\">\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M17 6h3v3\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"m20 6-4.5 4.5\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M4 7h3.2c1 0 1.95.4 2.66 1.1l6.28 6.3A3.75 3.75 0 0 0 18.82 15H20\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M17 15h3v3\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"m20 18-4.56-4.56\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M4 17h3.2c1 0 1.95-.4 2.66-1.1l1.54-1.54\"/></svg>\n </button>\n <button type=\"button\" class=\"ngxsp-nfx-chip\" [class.accent]=\"repeatMode !== 'off'\" (click)=\"cycleRepeat.emit()\" [attr.title]=\"repeatLabel\" [attr.aria-label]=\"repeatLabel\">\n @if (repeatMode === 'one') {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M17 4h3v3\"/>\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M20 7l-2.8-2.8A4 4 0 0 0 14.37 3H8a4 4 0 0 0-4 4v1\"/>\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M7 20H4v-3\"/>\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"m4 17 2.8 2.8A4 4 0 0 0 9.63 21H16a4 4 0 0 0 4-4v-1\"/>\n <text x=\"16.8\" y=\"17.6\" fill=\"currentColor\" stroke=\"none\" font-size=\"7.5\" font-weight=\"700\" text-anchor=\"middle\">1</text>\n </svg>\n } @else {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M17 4h3v3\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M20 7l-2.8-2.8A4 4 0 0 0 14.37 3H8a4 4 0 0 0-4 4v1\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M7 20H4v-3\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"m4 17 2.8 2.8A4 4 0 0 0 9.63 21H16a4 4 0 0 0 4-4v-1\"/></svg>\n }\n </button>\n }\n <button type=\"button\" class=\"ngxsp-nfx-chip\" (click)=\"toggleMenu('tracks')\" [attr.title]=\"tracksButtonLabel()\" [attr.aria-label]=\"tracksButtonLabel()\">\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M4 6.75h16M4 12h16M4 17.25h16\"/></svg>\n </button>\n <button type=\"button\" class=\"ngxsp-nfx-chip\" (click)=\"subtitles.emit()\" [attr.title]=\"captionsLabel()\" [attr.aria-label]=\"captionsLabel()\">\n @if (selectedTextTrackId !== null) {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><rect x=\"3.5\" y=\"5.5\" width=\"17\" height=\"13\" rx=\"2.5\" stroke-width=\"1.8\"/><path stroke-linecap=\"round\" stroke-width=\"1.8\" d=\"M8 11.25h2.5m2 0H14m2 0h.5M8 14.75h3.5m2 0H16\"/></svg>\n } @else {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><rect x=\"3.5\" y=\"5.5\" width=\"17\" height=\"13\" rx=\"2.5\" stroke-width=\"1.8\"/><path stroke-linecap=\"round\" stroke-width=\"1.8\" d=\"M8 11.25h2.5m2 0H14m2 0h.5M8 14.75h3.5m2 0H16\"/><path stroke-linecap=\"round\" stroke-width=\"1.8\" d=\"M6 6l12 12\"/></svg>\n }\n </button>\n <button type=\"button\" class=\"ngxsp-nfx-chip\" (click)=\"toggleMenu('speed')\" [attr.title]=\"playbackRateLabel()\" [attr.aria-label]=\"playbackRateLabel()\">\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M5.25 5.82c0-1.3 1.43-2.1 2.54-1.43l5.71 3.47a1.96 1.96 0 0 1 0 3.36L7.79 14.7a1.67 1.67 0 0 1-2.54-1.43V5.82Zm8.5 0c0-1.3 1.43-2.1 2.54-1.43L22 7.86a1.96 1.96 0 0 1 0 3.36l-5.71 3.47a1.67 1.67 0 0 1-2.54-1.43V5.82Z\"/></svg>\n </button>\n @if (isPiPSupported()) {\n <button type=\"button\" class=\"ngxsp-nfx-chip\" (click)=\"togglePiP()\" [attr.title]=\"pipLabel()\" [attr.aria-label]=\"pipLabel()\">\n @if (isPiPActive()) {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><rect x=\"3.5\" y=\"5.5\" width=\"17\" height=\"13\" rx=\"2.5\" stroke-width=\"1.8\"/><rect x=\"12.25\" y=\"11.25\" width=\"5.25\" height=\"4.5\" rx=\"1\" fill=\"currentColor\" stroke=\"none\"/></svg>\n } @else {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><rect x=\"3.5\" y=\"5.5\" width=\"17\" height=\"13\" rx=\"2.5\" stroke-width=\"1.8\"/><rect x=\"12.25\" y=\"11.25\" width=\"5.25\" height=\"4.5\" rx=\"1\" stroke-width=\"1.8\"/></svg>\n }\n </button>\n }\n </div>\n\n <input class=\"ngxsp-range ngxsp-nfx-range\" type=\"range\"\n min=\"0\" [max]=\"duration || 0\" step=\"0.1\"\n [value]=\"currentTime\"\n (input)=\"onScrub(($any($event.target).value))\"\n aria-label=\"Seek\"\n />\n\n <div class=\"ngxsp-nfx-metaRow\">\n <div class=\"ngxsp-nfx-spacer\"></div>\n <div class=\"ngxsp-nfx-label\">{{ keyboardHintLabel() }}</div>\n </div>\n\n <div class=\"ngxsp-buffer\" aria-hidden=\"true\">\n <div class=\"ngxsp-bufferFill\" [style.width.%]=\"bufferPercent()\"></div>\n </div>\n\n @if (openMenu === 'tracks') {\n <div class=\"ngxsp-menu\" (click)=\"$event.stopPropagation()\">\n <div class=\"ngxsp-menuSection\">\n <div class=\"ngxsp-menuTitle\">Captions</div>\n <button type=\"button\" class=\"ngxsp-menuItem\" [class.active]=\"selectedTextTrackId === null\" (click)=\"selectTextTrack(null)\">Off</button>\n @for (track of textTracks; track track.id) {\n <button type=\"button\" class=\"ngxsp-menuItem\" [class.active]=\"selectedTextTrackId === track.id\" (click)=\"selectTextTrack(track.id)\">\n {{ trackLabel(track) }}\n </button>\n }\n </div>\n\n <div class=\"ngxsp-menuSection\">\n <div class=\"ngxsp-menuTitle\">Audio</div>\n @if (audioTracks.length) {\n @for (track of audioTracks; track track.id) {\n <button type=\"button\" class=\"ngxsp-menuItem\" [class.active]=\"selectedAudioTrackId === track.id\" (click)=\"selectAudioTrack(track.id)\">\n {{ trackLabel(track) }}\n </button>\n }\n } @else {\n <div class=\"ngxsp-menuHint\">Single audio track</div>\n }\n </div>\n\n <div class=\"ngxsp-menuSection\">\n <div class=\"ngxsp-menuTitle\">Quality</div>\n <button type=\"button\" class=\"ngxsp-menuItem\" [class.active]=\"selectedVideoTrackId === null\" (click)=\"selectVideoTrack(null)\">Auto</button>\n @if (videoTracks.length) {\n @for (track of videoTracks; track track.id) {\n <button type=\"button\" class=\"ngxsp-menuItem\" [class.active]=\"selectedVideoTrackId === track.id\" (click)=\"selectVideoTrack(track.id)\">\n {{ qualityLabel(track) }}\n </button>\n }\n } @else {\n <div class=\"ngxsp-menuHint\">Single quality</div>\n }\n </div>\n </div>\n }\n\n @if (openMenu === 'speed') {\n <div class=\"ngxsp-menu ngxsp-menuCompact\" (click)=\"$event.stopPropagation()\">\n <div class=\"ngxsp-menuSection\">\n <div class=\"ngxsp-menuTitle\">Playback speed</div>\n @for (rate of playbackRates; track rate) {\n <button type=\"button\" class=\"ngxsp-menuItem\" [class.active]=\"playbackRate === rate\" (click)=\"setPlaybackRate(rate)\">\n {{ rate }}x\n </button>\n }\n </div>\n </div>\n }\n </div>\n } @else {\n <div class=\"ngxsp-controls\">\n <button type=\"button\" class=\"ngxsp-btn\" (click)=\"togglePlay()\" aria-label=\"Play/Pause\" title=\"Play/Pause\">\n @if (state?.kind==='playing') {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M7.5 5.25A1.25 1.25 0 0 1 8.75 6.5v11A1.25 1.25 0 0 1 6.25 17.5v-11A1.25 1.25 0 0 1 7.5 5.25Zm8 0a1.25 1.25 0 0 1 1.25 1.25v11a1.25 1.25 0 1 1-2.5 0v-11A1.25 1.25 0 0 1 15.5 5.25Z\"/></svg>\n } @else {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M8.25 6.82c0-1.3 1.43-2.1 2.54-1.43l8.12 4.93a1.96 1.96 0 0 1 0 3.36l-8.12 4.93a1.67 1.67 0 0 1-2.54-1.43V6.82Z\"/></svg>\n }\n </button>\n <button type=\"button\" class=\"ngxsp-btn\" (click)=\"seekRel(-10)\" aria-label=\"Seek backward 10 seconds\" title=\"Seek backward 10 seconds\">\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M9 5H5m0 0v4m0-4 4.5 4.5A7 7 0 1 1 5 12\"/></svg>\n <span class=\"ngxsp-iconBadge\">10</span>\n </button>\n <button type=\"button\" class=\"ngxsp-btn\" (click)=\"seekRel(10)\" aria-label=\"Seek forward 10 seconds\" title=\"Seek forward 10 seconds\">\n <span class=\"ngxsp-iconBadge\">10</span>\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M15 5h4m0 0v4m0-4-4.5 4.5A7 7 0 1 0 19 12\"/></svg>\n </button>\n @if (hasPlaylistControls) {\n <button type=\"button\" class=\"ngxsp-btn\" (click)=\"emitPrev()\" [disabled]=\"!canPrev\" aria-label=\"Previous item\" title=\"Previous item\"><svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M9.53 12 18 18.47v-12L9.53 12Zm-1.53 6.25a.75.75 0 0 0 1.25-.56V6.31a.75.75 0 0 0-1.25-.56L1.06 11.44a.75.75 0 0 0 0 1.12L8 18.25Z\"/></svg></button>\n <button type=\"button\" class=\"ngxsp-btn\" (click)=\"emitNext()\" [disabled]=\"!canNext\" aria-label=\"Next item\" title=\"Next item\"><svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M14.47 12 6 5.53v12L14.47 12ZM16 5.75a.75.75 0 0 0-1.25.56v11.38a.75.75 0 0 0 1.25.56l6.94-5.69a.75.75 0 0 0 0-1.12L16 5.75Z\"/></svg></button>\n <button type=\"button\" class=\"ngxsp-btn\" [class.accent]=\"shuffleActive\" (click)=\"toggleShuffle.emit()\" [attr.title]=\"shuffleLabel\" [attr.aria-label]=\"shuffleLabel\">\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M17 6h3v3\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"m20 6-4.5 4.5\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M4 7h3.2c1 0 1.95.4 2.66 1.1l6.28 6.3A3.75 3.75 0 0 0 18.82 15H20\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M17 15h3v3\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"m20 18-4.56-4.56\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M4 17h3.2c1 0 1.95-.4 2.66-1.1l1.54-1.54\"/></svg>\n </button>\n <button type=\"button\" class=\"ngxsp-btn\" [class.accent]=\"repeatMode !== 'off'\" (click)=\"cycleRepeat.emit()\" [attr.title]=\"repeatLabel\" [attr.aria-label]=\"repeatLabel\">\n @if (repeatMode === 'one') {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M17 4h3v3\"/>\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M20 7l-2.8-2.8A4 4 0 0 0 14.37 3H8a4 4 0 0 0-4 4v1\"/>\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M7 20H4v-3\"/>\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"m4 17 2.8 2.8A4 4 0 0 0 9.63 21H16a4 4 0 0 0 4-4v-1\"/>\n <text x=\"16.8\" y=\"17.6\" fill=\"currentColor\" stroke=\"none\" font-size=\"7.5\" font-weight=\"700\" text-anchor=\"middle\">1</text>\n </svg>\n } @else {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M17 4h3v3\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M20 7l-2.8-2.8A4 4 0 0 0 14.37 3H8a4 4 0 0 0-4 4v1\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M7 20H4v-3\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"m4 17 2.8 2.8A4 4 0 0 0 9.63 21H16a4 4 0 0 0 4-4v-1\"/></svg>\n }\n </button>\n }\n <button type=\"button\" class=\"ngxsp-btn\" (click)=\"toggleMenu('tracks')\" [attr.title]=\"tracksButtonLabel()\" [attr.aria-label]=\"tracksButtonLabel()\"><svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M4 6.75h16M4 12h16M4 17.25h16\"/></svg></button>\n <button type=\"button\" class=\"ngxsp-btn\" (click)=\"subtitles.emit()\" [attr.title]=\"captionsLabel()\" [attr.aria-label]=\"captionsLabel()\">\n @if (selectedTextTrackId !== null) {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><rect x=\"3.5\" y=\"5.5\" width=\"17\" height=\"13\" rx=\"2.5\" stroke-width=\"1.8\"/><path stroke-linecap=\"round\" stroke-width=\"1.8\" d=\"M8 11.25h2.5m2 0H14m2 0h.5M8 14.75h3.5m2 0H16\"/></svg>\n } @else {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><rect x=\"3.5\" y=\"5.5\" width=\"17\" height=\"13\" rx=\"2.5\" stroke-width=\"1.8\"/><path stroke-linecap=\"round\" stroke-width=\"1.8\" d=\"M8 11.25h2.5m2 0H14m2 0h.5M8 14.75h3.5m2 0H16\"/><path stroke-linecap=\"round\" stroke-width=\"1.8\" d=\"M6 6l12 12\"/></svg>\n }\n </button>\n <button type=\"button\" class=\"ngxsp-btn\" (click)=\"toggleMenu('speed')\" [attr.title]=\"playbackRateLabel()\" [attr.aria-label]=\"playbackRateLabel()\"><svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M5.25 5.82c0-1.3 1.43-2.1 2.54-1.43l5.71 3.47a1.96 1.96 0 0 1 0 3.36L7.79 14.7a1.67 1.67 0 0 1-2.54-1.43V5.82Zm8.5 0c0-1.3 1.43-2.1 2.54-1.43L22 7.86a1.96 1.96 0 0 1 0 3.36l-5.71 3.47a1.67 1.67 0 0 1-2.54-1.43V5.82Z\"/></svg></button>\n @if (isPiPSupported()) {\n <button type=\"button\" class=\"ngxsp-btn\" (click)=\"togglePiP()\" [attr.title]=\"pipLabel()\" [attr.aria-label]=\"pipLabel()\">\n @if (isPiPActive()) {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><rect x=\"3.5\" y=\"5.5\" width=\"17\" height=\"13\" rx=\"2.5\" stroke-width=\"1.8\"/><rect x=\"12.25\" y=\"11.25\" width=\"5.25\" height=\"4.5\" rx=\"1\" fill=\"currentColor\" stroke=\"none\"/></svg>\n } @else {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><rect x=\"3.5\" y=\"5.5\" width=\"17\" height=\"13\" rx=\"2.5\" stroke-width=\"1.8\"/><rect x=\"12.25\" y=\"11.25\" width=\"5.25\" height=\"4.5\" rx=\"1\" stroke-width=\"1.8\"/></svg>\n }\n </button>\n }\n <button type=\"button\" class=\"ngxsp-btn\" (click)=\"toggleMute()\" [attr.title]=\"volumeLabel()\" [attr.aria-label]=\"volumeLabel()\">\n @if (volume <= 0.001) {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M11 5 6.5 9H3.75v6h2.75L11 19V5Zm3.5 5.5 5 5m0-5-5 5\"/></svg>\n } @else if (volume < 0.5) {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M11 5 6.5 9H3.75v6h2.75L11 19V5Zm4.5 4.75a3.5 3.5 0 0 1 0 4.95\"/></svg>\n } @else {\n <svg class=\"ngxsp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" d=\"M11 5 6.5 9H3.75v6h2.75L11 19V5Zm4.5 4.75a3.5 3.5 0 0 1 0 4.95m2.75-7.7a7.5 7.5 0 0 1 0 10.45\"/></svg>\n }\n </button>\n\n <input class=\"ngxsp-range\" type=\"range\"\n min=\"0\" [max]=\"duration || 0\" step=\"0.1\"\n [value]=\"currentTime\"\n (input)=\"onScrub(($any($event.target).value))\"\n />\n\n <div class=\"ngxsp-time\">{{fmtTime(currentTime)}} / {{fmtTime(duration)}}</div>\n\n <input class=\"ngxsp-range\" style=\"max-width:140px\" type=\"range\"\n min=\"0\" max=\"1\" step=\"0.01\"\n [value]=\"volume\"\n (input)=\"onVolume(($any($event.target).value))\"\n aria-label=\"Volume\"\n />\n </div>\n }\n\n </div>\n </div>\n `,\n styles: [`\n .ngxsp-root{position:relative;width:100%;height:100%;background:#000;color:#fff;font-family:system-ui,sans-serif;}\n .ngxsp-video{position:relative;width:100%;height:100%;isolation:isolate}\n .ngxsp-video video{width:100%;height:100%;display:block;background:#000;object-fit:contain;}\n .ngxsp-controls{display:flex;gap:8px;align-items:center;padding:10px;flex-wrap:wrap}\n .ngxsp-btn{background:rgba(255,255,255,.12);border:1px solid rgba(255,255,255,.2);color:#fff;border-radius:10px;padding:8px 10px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:8px}\n .ngxsp-btn.accent{background:rgba(86,63,27,.5);border-color:rgba(232,190,118,.24);color:#f3d59c}\n .ngxsp-icon{width:18px;height:18px;display:block;flex:0 0 auto}\n .ngxsp-icon-lg{width:32px;height:32px}\n .ngxsp-iconBadge{font-size:11px;font-weight:800;line-height:1}\n .ngxsp-range{flex:1;min-width:160px}\n .ngxsp-time{opacity:.85;font-variant-numeric:tabular-nums}\n .ngxsp-overlay{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;background:rgba(0,0,0,.7);padding:20px;text-align:center}\n .ngxsp-debug{position:absolute; top:12px; right:12px; z-index:6; margin:0; max-width:min(420px, calc(100% - 24px)); opacity:0; transform:translateY(-6px); transition:opacity .18s ease, transform .18s ease}\n .ngxsp-root.ngxsp-ui-visible .ngxsp-debug,\n .ngxsp-debug[open]{opacity:1; transform:translateY(0)}\n .ngxsp-debug summary{cursor:pointer; list-style:none; background:rgba(10,10,10,.82); border:1px solid rgba(255,255,255,.16); border-radius:999px; padding:8px 12px; width:max-content; margin-left:auto;}\n .ngxsp-debug[open]{background:rgba(10,10,10,.92); border:1px solid rgba(255,255,255,.16); border-radius:16px; padding:12px; max-height:calc(100% - 24px); overflow:auto;}\n .ngxsp-overlay-title{font-size:18px;font-weight:600}\n .ngxsp-overlay-msg{opacity:.9;max-width:720px}\n\n /* Netflix skin */\n .ngxsp-skin-netflix .ngxsp-video{position:relative}\n .ngxsp-skin-netflix .ngxsp-video::before{content:''; position:absolute; inset:0; background:radial-gradient(circle at center, rgba(0,0,0,0) 32%, rgba(0,0,0,.22) 100%); opacity:.85; pointer-events:none; z-index:0}\n .ngxsp-skin-netflix .ngxsp-video::after{content:''; position:absolute; inset:auto 0 0 0; height:180px; background:linear-gradient(to top, rgba(0,0,0,.9), rgba(0,0,0,.48) 38%, rgba(0,0,0,0)); opacity:0; transition:opacity .18s ease; pointer-events:none}\n .ngxsp-skin-netflix.ngxsp-ui-visible .ngxsp-video::after{opacity:1}\n .ngxsp-nfx-top{position:absolute;left:0;right:0;top:0;display:flex;align-items:flex-start;gap:12px;padding:18px 18px 22px;background:linear-gradient(to bottom, rgba(0,0,0,.78), rgba(0,0,0,0));pointer-events:none; opacity:0; transition:opacity .18s ease; z-index:3}\n .ngxsp-root.ngxsp-ui-visible .ngxsp-nfx-top{opacity:1}\n .ngxsp-nfx-titleWrap{display:flex; flex-direction:column; gap:3px}\n .ngxsp-nfx-eyebrow{font-size:11px; text-transform:uppercase; letter-spacing:.16em; opacity:.62}\n .ngxsp-nfx-title{font-weight:700;letter-spacing:.01em;opacity:.98;pointer-events:none;text-shadow:0 1px 2px rgba(0,0,0,.6); font-size:18px}\n .ngxsp-nfx-topHint{font-size:12px; opacity:.6; padding-top:4px}\n .ngxsp-nfx-status{font-size:11px; text-transform:uppercase; letter-spacing:.12em; padding:7px 10px; border-radius:999px; border:1px solid rgba(255,255,255,.12); background:rgba(255,255,255,.06); opacity:.78}\n .ngxsp-nfx-spacer{flex:1}\n .ngxsp-nfx-centerDock{position:absolute; inset:0; margin:auto; width:max-content; height:max-content; display:flex; align-items:center; gap:14px; z-index:4; opacity:0; transform:translateY(10px) scale(.98); transition:opacity .18s ease, transform .18s ease}\n .ngxsp-root.ngxsp-ui-visible .ngxsp-nfx-centerDock{opacity:1; transform:translateY(0) scale(1)}\n .ngxsp-nfx-center{height:96px;width:96px;border-radius:999px;border:1px solid rgba(255,255,255,.26);background:rgba(248,248,248,.12);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer; backdrop-filter:blur(16px); box-shadow:0 20px 60px rgba(0,0,0,.35)}\n .ngxsp-nfx-side{height:58px; min-width:58px; border-radius:999px; border:1px solid rgba(255,255,255,.18); background:rgba(8,8,8,.42); color:#fff; padding:0 18px; cursor:pointer; backdrop-filter:blur(10px); font-weight:700; font-size:14px; display:inline-flex; align-items:center; justify-content:center; gap:8px}\n .ngxsp-nfx-side-wide{min-width:78px}\n .ngxsp-nfx-bottom{position:absolute;left:14px;right:14px;bottom:14px;padding:12px 14px 14px;background:linear-gradient(to top, rgba(7,7,7,.92), rgba(7,7,7,.7));border:1px solid rgba(255,255,255,.1);border-radius:20px;gap:10px;flex-direction:column;align-items:stretch;backdrop-filter:blur(20px);z-index:4;opacity:0;transform:translateY(12px);transition:opacity .18s ease, transform .18s ease;box-shadow:0 16px 50px rgba(0,0,0,.32)}\n .ngxsp-root.ngxsp-ui-visible .ngxsp-nfx-bottom{opacity:1; transform:translateY(0)}\n .ngxsp-nfx-metaRow{display:flex; align-items:center; gap:10px; width:100%}\n .ngxsp-nfx-time{opacity:.92;font-variant-numeric:tabular-nums;font-size:12px; font-weight:600}\n .ngxsp-nfx-range{min-width:100%}\n .ngxsp-nfx-volumeGroup{display:inline-flex;align-items:center;gap:8px}\n .ngxsp-nfx-volume{max-width:0;min-width:0;opacity:0;transform:scaleX(.72);transform-origin:left center;pointer-events:none;transition:opacity .18s ease, transform .18s ease, max-width .18s ease}\n .ngxsp-nfx-volumeGroup:hover .ngxsp-nfx-volume,\n .ngxsp-nfx-volumeGroup:focus-within .ngxsp-nfx-volume{max-width:160px;min-width:96px;opacity:1;transform:scaleX(1);pointer-events:auto}\n .ngxsp-nfx-label{font-size:12px; opacity:.72}\n .ngxsp-nfx-chip{background:rgba(255,255,255,.08); border:1px solid rgba(255,255,255,.16); color:#fff; border-radius:999px; padding:8px 12px; cursor:pointer; font-size:12px; font-weight:700; display:inline-flex; align-items:center; justify-content:center; gap:8px}\n .ngxsp-nfx-chip.accent{background:rgba(86,63,27,.5); border-color:rgba(232,190,118,.24); color:#f3d59c}\n .ngxsp-nfx-chip-quiet{background:rgba(255,255,255,.05)}\n .ngxsp-nfx-center:hover,\n .ngxsp-nfx-side:hover,\n .ngxsp-nfx-chip:hover{border-color:rgba(255,255,255,.3); background:rgba(24,24,24,.62)}\n .ngxsp-nfx-center:disabled,\n .ngxsp-nfx-side:disabled{opacity:.38; cursor:not-allowed}\n .ngxsp-range{appearance:none; height:4px; border-radius:999px; background:linear-gradient(to right, rgba(255,255,255,.95), rgba(255,255,255,.28)); outline:none}\n .ngxsp-range::-webkit-slider-thumb{appearance:none; width:14px; height:14px; border-radius:50%; background:#fff; box-shadow:0 0 0 3px rgba(255,255,255,.16)}\n .ngxsp-range::-moz-range-thumb{width:14px; height:14px; border:none; border-radius:50%; background:#fff; box-shadow:0 0 0 3px rgba(255,255,255,.16)}\n .ngxsp-range::-moz-range-track{height:4px; border:none; border-radius:999px; background:rgba(255,255,255,.22)}\n .ngxsp-buffer{height:4px; border-radius:999px; background:rgba(255,255,255,.08); overflow:hidden}\n .ngxsp-bufferFill{height:100%; border-radius:999px; background:linear-gradient(to right, rgba(245,245,245,.28), rgba(245,245,245,.82))}\n .ngxsp-menu{display:grid; grid-template-columns:repeat(auto-fit, minmax(180px, 1fr)); gap:14px; padding-top:6px}\n .ngxsp-menuCompact{grid-template-columns:minmax(180px, 240px)}\n .ngxsp-menuSection{display:flex; flex-direction:column; gap:6px}\n .ngxsp-menuTitle{font-size:11px; text-transform:uppercase; letter-spacing:.14em; opacity:.58}\n .ngxsp-menuItem{background:rgba(255,255,255,.05); border:1px solid rgba(255,255,255,.1); color:#fff; border-radius:12px; padding:9px 11px; text-align:left; cursor:pointer; font-size:12px}\n .ngxsp-menuItem.active{border-color:rgba(232,190,118,.28); background:rgba(86,63,27,.38); color:#f3d59c}\n .ngxsp-menuHint{font-size:12px; opacity:.62; padding:8px 2px}\n `],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class SmartPlayerComponent implements OnInit, OnDestroy {\n @Input({ required: true }) config!: PlayerConfig;\n @Input() plugins: PlayerPlugin[] = [];\n @Input() ports?: Partial<PlayerPorts>;\n @Input() engines?: EngineFactory[];\n @Input() debug = false;\n /** Optional start time (seconds) used for \"resume playback\". */\n @Input() startTimeSec: number | null = null;\n @Input() canPrev = false;\n @Input() canNext = false;\n @Input() shuffleActive = false;\n @Input() shuffleLabel = 'Shuffle: off';\n @Input() repeatMode: 'off' | 'one' | 'all' = 'off';\n @Input() repeatLabel = 'Repeat: off';\n @Input() hasPlaylistControls = false;\n\n @Output() ended = new EventEmitter<void>();\n @Output() videoSize = new EventEmitter<{ width: number; height: number }>();\n /** Emits playback position updates. */\n @Output() progress = new EventEmitter<{ currentTime: number; duration: number }>();\n @Output() prev = new EventEmitter<void>();\n @Output() next = new EventEmitter<void>();\n @Output() toggleShuffle = new EventEmitter<void>();\n @Output() cycleRepeat = new EventEmitter<void>();\n @Output() subtitles = new EventEmitter<void>();\n\n @ViewChild('video', { static: true }) videoEl!: ElementRef<HTMLVideoElement>;\n\n state: PlayerState | null = null;\n\n duration = 0;\n currentTime = 0;\n volume = 1;\n private lastVolumeBeforeMute = 1;\n playbackRate = 1;\n bufferAheadSec = 0;\n tracks: MediaTrack[] = [];\n openMenu: 'tracks' | 'speed' | null = null;\n controlsVisible = true;\n\n readonly playbackRates = [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];\n\n private facade = new PlayerFacade();\n private sub = new Subscription();\n private hideControlsTimer: ReturnType<typeof setTimeout> | null = null;\n\n get uiSkin(): 'classic' | 'netflix' {\n return (((this.config as any)?.ui?.skin as any) ?? 'classic') === 'netflix' ? 'netflix' : 'classic';\n }\n\n get uiVisible(): boolean {\n const kind = this.state?.kind;\n return this.controlsVisible || this.openMenu !== null || kind === 'paused' || kind === 'loading' || kind === 'buffering' || kind === 'error';\n }\n\n get displayTitle(): string {\n const ui: any = (this.config as any)?.ui;\n const src: any = (this.config as any)?.source;\n return (\n ui?.title ??\n ui?.displayTitle ??\n src?.title ??\n src?.contentId ??\n 'Player'\n );\n }\n\n get audioTracks(): MediaTrack[] {\n return this.tracks.filter((track) => track.kind === 'audio');\n }\n\n get textTracks(): MediaTrack[] {\n return this.tracks.filter((track) => track.kind === 'text');\n }\n\n get videoTracks(): MediaTrack[] {\n return this.tracks.filter((track) => track.kind === 'video');\n }\n\n get selectedAudioTrackId(): string | null {\n return this.audioTracks.find((track) => track.active)?.id ?? null;\n }\n\n get selectedTextTrackId(): string | null {\n return this.textTracks.find((track) => track.active)?.id ?? null;\n }\n\n get selectedVideoTrackId(): string | null {\n return this.videoTracks.find((track) => track.active)?.id ?? null;\n }\n\n ngOnInit(): void {\n const plugins: PlayerPlugin[] = [...(this.plugins ?? [])];\n\n // v1.11: SSAI (server-side stitched streams) – rewrite manifest URL if configured.\n if ((this.config as any)?.ads?.enabled && (this.config as any)?.ads?.mode === 'ssai') {\n plugins.push(createSsaiManifestRewritePlugin());\n }\n\n this.facade.attach(this.videoEl.nativeElement, plugins, this.config, this.ports, this.engines);\n\n this.sub.add(this.facade.state$.subscribe((s: PlayerState) => {\n this.state = s;\n this.syncControlVisibility();\n }));\n this.sub.add(this.facade.time$.subscribe((t: any) => {\n this.currentTime = t.currentTime;\n this.duration = t.duration;\n this.progress.emit({ currentTime: this.currentTime, duration: this.duration });\n }));\n this.sub.add(this.facade.bufferAheadSec$.subscribe((value) => {\n this.bufferAheadSec = value;\n }));\n this.sub.add(this.facade.tracks$.subscribe((tracks) => {\n this.tracks = tracks;\n }));\n\n this.facade.setVolume(this.volume);\n this.facade.load(this.config)\n .then(async () => {\n const start = Number(this.startTimeSec ?? 0);\n if (Number.isFinite(start) && start > 0.2) {\n try {\n const safe = Math.max(0, Math.min(start, (this.duration || start + 1) - 0.3));\n await this.facade.seekTo(safe);\n } catch { /* ignore */ }\n }\n await this.facade.play().catch(() => {});\n })\n .catch(() => {});\n }\n\n ngOnDestroy(): void {\n this.clearHideControlsTimer();\n this.sub.unsubscribe();\n this.facade.destroy();\n }\n\n tryPlay(): void {\n void this.facade.play().catch(() => {});\n }\n\n get debugJson(): string {\n try {\n const d = (this.facade as any).diagnostics?.() ?? {};\n const tel = this.ports?.telemetry?.export?.();\n return JSON.stringify({ ...d, telemetry: tel }, null, 2);\n } catch {\n return '{}';\n }\n }\n\n get autoplayBlocked(): boolean {\n const s: any = this.state as any;\n return s?.kind === 'error' && s?.error?.code === 'AUTOPLAY_BLOCKED';\n }\n\n async copyDebug(): Promise<void> {\n try {\n await navigator.clipboard.writeText(this.debugJson);\n } catch {\n // clipboard may be unavailable\n }\n }\n\n downloadDebug(): void {\n try {\n const blob = new Blob([this.debugJson], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'ngxsp-diagnostics.json';\n a.click();\n setTimeout(() => URL.revokeObjectURL(url), 0);\n } catch {\n // ignore\n }\n }\n\n togglePlay(): void {\n if (this.state?.kind === 'playing') this.facade.pause();\n else this.facade.play();\n }\n\n seekRel(deltaSec: number): void {\n this.facade.seekTo(Math.max(0, this.currentTime + deltaSec));\n }\n\n onScrub(value: number): void {\n const v = Number(value);\n if (Number.isFinite(v)) this.facade.seekTo(v);\n }\n\n onVolume(value: number): void {\n const v = Math.max(0, Math.min(1, Number(value)));\n this.volume = Number.isFinite(v) ? v : this.volume;\n if (this.volume > 0) this.lastVolumeBeforeMute = this.volume;\n this.facade.setVolume(this.volume);\n }\n\n toggleMute(): void {\n if (this.volume <= 0.001) {\n this.volume = Math.max(0.5, this.lastVolumeBeforeMute || 0.5);\n } else {\n this.lastVolumeBeforeMute = this.volume;\n this.volume = 0;\n }\n this.facade.setVolume(this.volume);\n this.onInteraction();\n }\n\n async setPlaybackRate(rate: number): Promise<void> {\n this.playbackRate = rate;\n this.openMenu = null;\n this.onInteraction();\n await this.facade.setRate(rate);\n }\n\n reload(): void {\n this.facade.reload().catch(() => void 0);\n }\n\n\n onLoadedMetadata(): void {\n const v = this.videoEl?.nativeElement;\n if (!v) return;\n const width = v.videoWidth || 0;\n const height = v.videoHeight || 0;\n if (width > 0 && height > 0) {\n this.videoSize.emit({ width, height });\n }\n }\n\n onEnded(): void {\n // facade already emits 'ended' based on engine events; this output lets host apps chain playlists.\n this.ended.emit();\n }\n\n toggleFullscreen(): void {\n const host = this.videoEl.nativeElement.parentElement;\n if (!host) return;\n\n const doc: any = document;\n const el: any = host;\n\n if (!doc.fullscreenElement && el.requestFullscreen) {\n el.requestFullscreen().catch(() => void 0);\n } else if (doc.exitFullscreen) {\n doc.exitFullscreen().catch(() => void 0);\n }\n }\n\n toggleMenu(menu: 'tracks' | 'speed'): void {\n this.openMenu = this.openMenu === menu ? null : menu;\n this.onInteraction(this.openMenu !== null);\n }\n\n async selectTextTrack(trackId: string | null): Promise<void> {\n this.openMenu = null;\n this.onInteraction();\n await this.facade.setTextTrack(trackId);\n }\n\n async selectAudioTrack(trackId: string): Promise<void> {\n this.openMenu = null;\n this.onInteraction();\n await this.facade.setAudioTrack(trackId);\n }\n\n async selectVideoTrack(trackId: string | null): Promise<void> {\n this.openMenu = null;\n this.onInteraction();\n await this.facade.setVideoTrack(trackId);\n }\n\n async togglePiP(): Promise<void> {\n const video = this.videoEl.nativeElement as HTMLVideoElement & {\n requestPictureInPicture?: () => Promise<unknown>;\n disablePictureInPicture?: boolean;\n };\n const doc = document as Document & {\n pictureInPictureElement?: Element | null;\n pictureInPictureEnabled?: boolean;\n exitPictureInPicture?: () => Promise<void>;\n };\n\n try {\n if (doc.pictureInPictureElement) {\n await doc.exitPictureInPicture?.();\n } else {\n await video.requestPictureInPicture?.();\n }\n } catch {\n // Ignore unsupported or blocked PiP transitions.\n }\n this.onInteraction();\n }\n\n isPiPSupported(): boolean {\n const video = this.videoEl?.nativeElement as (HTMLVideoElement & { disablePictureInPicture?: boolean }) | undefined;\n const doc = document as Document & { pictureInPictureEnabled?: boolean };\n return !!doc.pictureInPictureEnabled && !video?.disablePictureInPicture;\n }\n\n pipLabel(): string {\n const doc = document as Document & { pictureInPictureElement?: Element | null };\n return doc.pictureInPictureElement ? 'Exit PiP' : 'PiP';\n }\n\n isPiPActive(): boolean {\n const doc = document as Document & { pictureInPictureElement?: Element | null };\n return !!doc.pictureInPictureElement;\n }\n\n captionsLabel(): string {\n return this.selectedTextTrackId !== null ? 'Captions on' : 'Load subtitles';\n }\n\n volumeLabel(): string {\n if (this.volume <= 0.001) return 'Muted';\n if (this.volume < 0.5) return 'Vol';\n return 'Volume';\n }\n\n volumeIcon(): string {\n if (this.volume <= 0.001) return '🔇';\n if (this.volume < 0.5) return '🔉';\n return '🔊';\n }\n\n playbackRateLabel(): string {\n return `${this.playbackRate}x`;\n }\n\n statusLabel(): string {\n const kind = this.state?.kind;\n if (kind === 'playing') return this.playbackRate === 1 ? 'Playing' : `Playing ${this.playbackRate}x`;\n if (kind === 'paused') return 'Paused';\n if (kind === 'buffering') return 'Buffering';\n if (kind === 'loading') return 'Loading';\n if (kind === 'ended') return 'Ended';\n if (kind === 'error') return 'Error';\n return 'Ready';\n }\n\n keyboardHintLabel(): string {\n return 'Space play • Arrows seek • F fullscreen';\n }\n\n tracksButtonLabel(): string {\n return this.textTracks.length || this.audioTracks.length ? 'Tracks' : 'Media';\n }\n\n trackLabel(track: MediaTrack): string {\n return track.label || track.language || track.id;\n }\n\n qualityLabel(track: MediaTrack): string {\n if (track.label) return track.label;\n const parts: string[] = [];\n if (track.height) parts.push(`${track.height}p`);\n if (track.bandwidth) parts.push(`${(track.bandwidth / 1_000_000).toFixed(1)} Mbps`);\n return parts.join(' • ') || track.id;\n }\n\n bufferPercent(): number {\n if (!this.duration || !Number.isFinite(this.duration) || this.duration <= 0) return 0;\n return Math.max(0, Math.min(100, (this.bufferAheadSec / this.duration) * 100));\n }\n\n fmtTime(sec: number): string {\n const s = Math.max(0, Math.floor(sec || 0));\n const h = Math.floor(s / 3600);\n const m = Math.floor((s % 3600) / 60);\n const r = s % 60;\n if (h > 0) return `${h}:${String(m).padStart(2,'0')}:${String(r).padStart(2,'0')}`;\n return `${m}:${String(r).padStart(2,'0')}`;\n }\n\n noop(): void {}\n onInteraction(persist = false): void {\n this.controlsVisible = true;\n this.clearHideControlsTimer();\n if (!persist && this.state?.kind === 'playing' && this.openMenu === null) {\n this.hideControlsTimer = setTimeout(() => {\n this.controlsVisible = false;\n }, 2200);\n }\n }\n\n onPointerLeave(): void {\n if (this.state?.kind === 'playing' && this.openMenu === null) {\n this.clearHideControlsTimer();\n this.controlsVisible = false;\n }\n }\n\n onRootClick(event: MouseEvent): void {\n const target = event.target as HTMLElement | null;\n if (!target) return;\n if (target.closest('.ngxsp-menu, .ngxsp-nfx-chip, .ngxsp-btn, .ngxsp-debug, .ngxsp-nfx-side, .ngxsp-nfx-center, .ngxsp-range')) {\n return;\n }\n this.openMenu = null;\n this.onInteraction();\n }\n\n @HostListener('document:keydown.escape')\n onEscape(): void {\n if (this.openMenu !== null) {\n this.openMenu = null;\n this.onInteraction();\n }\n }\n\n @HostListener('document:keydown', ['$event'])\n onKeydown(event: KeyboardEvent): void {\n if (this.isInteractiveTarget(event.target)) return;\n\n if (event.code === 'Space') {\n event.preventDefault();\n this.togglePlay();\n this.onInteraction();\n return;\n }\n if (event.code === 'ArrowLeft') {\n event.preventDefault();\n this.seekRel(-10);\n this.onInteraction();\n return;\n }\n if (event.code === 'ArrowRight') {\n event.preventDefault();\n this.seekRel(10);\n this.onInteraction();\n return;\n }\n if (event.code === 'KeyF') {\n event.preventDefault();\n this.toggleFullscreen();\n this.onInteraction();\n return;\n }\n if (event.code === 'KeyM') {\n event.preventDefault();\n this.toggleMute();\n }\n }\n\n private syncControlVisibility(): void {\n if (this.state?.kind === 'playing' && this.openMenu === null) {\n this.onInteraction();\n return;\n }\n this.clearHideControlsTimer();\n this.controlsVisible = true;\n }\n\n private clearHideControlsTimer(): void {\n if (this.hideControlsTimer) {\n clearTimeout(this.hideControlsTimer);\n this.hideControlsTimer = null;\n }\n }\n\n private isInteractiveTarget(target: EventTarget | null): boolean {\n const el = target as HTMLElement | null;\n if (!el) return false;\n return !!el.closest('input, button, textarea, select, summary, [contenteditable=\"true\"]');\n }\n\nerrorTitle(): string {\n const s: any = this.state as any;\n const code: string = s?.error?.code ?? s?.error?.name ?? 'UNKNOWN';\n if (code === 'ENGINE_MISSING') return 'Playback engine missing';\n if (code === 'AUTOPLAY_BLOCKED') return 'Click to start playback';\n if (code === 'NETWORK_HTTP') return 'Network error';\n if (code === 'DECODE_ERROR') return 'Decode error';\n if (code === 'SRC_NOT_SUPPORTED') return 'Format not supported';\n return 'Playback error';\n}\n\nerrorHint(): string {\n const s: any = this.state as any;\n const code: string = s?.error?.code ?? s?.error?.name ?? 'UNKNOWN';\n if (code === 'ENGINE_MISSING') {\n return 'This source requires an additional engine. Install @ngxsp/engine-shaka and pass it via [engines].';\n }\n if (code === 'AUTOPLAY_BLOCKED') return 'Your browser blocked autoplay. Press Play to start.';\n if (code === 'NETWORK_HTTP') return 'Check your connection or try again.';\n return s?.error?.message ? String(s.error.message) : 'Unknown error';\n}\n\nasync onRetry(): Promise<void> {\n try { await this.facade.reload(); } catch {}\n}\n\nasync onUserPlay(): Promise<void> {\n try { await this.facade.play(); } catch {}\n}\n\n\nemitNext(): void {\n this.next.emit();\n}\n\nemitPrev(): void {\n this.prev.emit();\n}\n\n}\n","import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\nimport { SubtitleProvider } from '@ngxsp/core';\nimport { MetadataProvider } from '@ngxsp/core';\n\nexport interface SmartPlayerProvidersConfig {\n subtitleProviders?: SubtitleProvider[];\n metadataProviders?: MetadataProvider[];\n}\n\n/**\n * Register provider implementations for subtitles/metadata.\n * You can call this in your app `bootstrapApplication()` providers array.\n */\nexport function provideSmartPlayer(cfg: SmartPlayerProvidersConfig): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: 'SMART_PLAYER_SUBTITLE_PROVIDERS', useValue: cfg.subtitleProviders ?? [] },\n { provide: 'SMART_PLAYER_METADATA_PROVIDERS', useValue: cfg.metadataProviders ?? [] }\n ]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { PlayerConfig, PlayerPlugin, PlayerPorts, EngineFactory, PlayerState, MediaTrack, SubtitleProvider, MetadataProvider } from '@ngxsp/core';
|
|
2
|
+
export * from '@ngxsp/core';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { OnInit, OnDestroy, EventEmitter, ElementRef, EnvironmentProviders } from '@angular/core';
|
|
5
|
+
|
|
6
|
+
declare class SmartPlayerComponent implements OnInit, OnDestroy {
|
|
7
|
+
config: PlayerConfig;
|
|
8
|
+
plugins: PlayerPlugin[];
|
|
9
|
+
ports?: Partial<PlayerPorts>;
|
|
10
|
+
engines?: EngineFactory[];
|
|
11
|
+
debug: boolean;
|
|
12
|
+
/** Optional start time (seconds) used for "resume playback". */
|
|
13
|
+
startTimeSec: number | null;
|
|
14
|
+
canPrev: boolean;
|
|
15
|
+
canNext: boolean;
|
|
16
|
+
shuffleActive: boolean;
|
|
17
|
+
shuffleLabel: string;
|
|
18
|
+
repeatMode: 'off' | 'one' | 'all';
|
|
19
|
+
repeatLabel: string;
|
|
20
|
+
hasPlaylistControls: boolean;
|
|
21
|
+
ended: EventEmitter<void>;
|
|
22
|
+
videoSize: EventEmitter<{
|
|
23
|
+
width: number;
|
|
24
|
+
height: number;
|
|
25
|
+
}>;
|
|
26
|
+
/** Emits playback position updates. */
|
|
27
|
+
progress: EventEmitter<{
|
|
28
|
+
currentTime: number;
|
|
29
|
+
duration: number;
|
|
30
|
+
}>;
|
|
31
|
+
prev: EventEmitter<void>;
|
|
32
|
+
next: EventEmitter<void>;
|
|
33
|
+
toggleShuffle: EventEmitter<void>;
|
|
34
|
+
cycleRepeat: EventEmitter<void>;
|
|
35
|
+
subtitles: EventEmitter<void>;
|
|
36
|
+
videoEl: ElementRef<HTMLVideoElement>;
|
|
37
|
+
state: PlayerState | null;
|
|
38
|
+
duration: number;
|
|
39
|
+
currentTime: number;
|
|
40
|
+
volume: number;
|
|
41
|
+
private lastVolumeBeforeMute;
|
|
42
|
+
playbackRate: number;
|
|
43
|
+
bufferAheadSec: number;
|
|
44
|
+
tracks: MediaTrack[];
|
|
45
|
+
openMenu: 'tracks' | 'speed' | null;
|
|
46
|
+
controlsVisible: boolean;
|
|
47
|
+
readonly playbackRates: number[];
|
|
48
|
+
private facade;
|
|
49
|
+
private sub;
|
|
50
|
+
private hideControlsTimer;
|
|
51
|
+
get uiSkin(): 'classic' | 'netflix';
|
|
52
|
+
get uiVisible(): boolean;
|
|
53
|
+
get displayTitle(): string;
|
|
54
|
+
get audioTracks(): MediaTrack[];
|
|
55
|
+
get textTracks(): MediaTrack[];
|
|
56
|
+
get videoTracks(): MediaTrack[];
|
|
57
|
+
get selectedAudioTrackId(): string | null;
|
|
58
|
+
get selectedTextTrackId(): string | null;
|
|
59
|
+
get selectedVideoTrackId(): string | null;
|
|
60
|
+
ngOnInit(): void;
|
|
61
|
+
ngOnDestroy(): void;
|
|
62
|
+
tryPlay(): void;
|
|
63
|
+
get debugJson(): string;
|
|
64
|
+
get autoplayBlocked(): boolean;
|
|
65
|
+
copyDebug(): Promise<void>;
|
|
66
|
+
downloadDebug(): void;
|
|
67
|
+
togglePlay(): void;
|
|
68
|
+
seekRel(deltaSec: number): void;
|
|
69
|
+
onScrub(value: number): void;
|
|
70
|
+
onVolume(value: number): void;
|
|
71
|
+
toggleMute(): void;
|
|
72
|
+
setPlaybackRate(rate: number): Promise<void>;
|
|
73
|
+
reload(): void;
|
|
74
|
+
onLoadedMetadata(): void;
|
|
75
|
+
onEnded(): void;
|
|
76
|
+
toggleFullscreen(): void;
|
|
77
|
+
toggleMenu(menu: 'tracks' | 'speed'): void;
|
|
78
|
+
selectTextTrack(trackId: string | null): Promise<void>;
|
|
79
|
+
selectAudioTrack(trackId: string): Promise<void>;
|
|
80
|
+
selectVideoTrack(trackId: string | null): Promise<void>;
|
|
81
|
+
togglePiP(): Promise<void>;
|
|
82
|
+
isPiPSupported(): boolean;
|
|
83
|
+
pipLabel(): string;
|
|
84
|
+
isPiPActive(): boolean;
|
|
85
|
+
captionsLabel(): string;
|
|
86
|
+
volumeLabel(): string;
|
|
87
|
+
volumeIcon(): string;
|
|
88
|
+
playbackRateLabel(): string;
|
|
89
|
+
statusLabel(): string;
|
|
90
|
+
keyboardHintLabel(): string;
|
|
91
|
+
tracksButtonLabel(): string;
|
|
92
|
+
trackLabel(track: MediaTrack): string;
|
|
93
|
+
qualityLabel(track: MediaTrack): string;
|
|
94
|
+
bufferPercent(): number;
|
|
95
|
+
fmtTime(sec: number): string;
|
|
96
|
+
noop(): void;
|
|
97
|
+
onInteraction(persist?: boolean): void;
|
|
98
|
+
onPointerLeave(): void;
|
|
99
|
+
onRootClick(event: MouseEvent): void;
|
|
100
|
+
onEscape(): void;
|
|
101
|
+
onKeydown(event: KeyboardEvent): void;
|
|
102
|
+
private syncControlVisibility;
|
|
103
|
+
private clearHideControlsTimer;
|
|
104
|
+
private isInteractiveTarget;
|
|
105
|
+
errorTitle(): string;
|
|
106
|
+
errorHint(): string;
|
|
107
|
+
onRetry(): Promise<void>;
|
|
108
|
+
onUserPlay(): Promise<void>;
|
|
109
|
+
emitNext(): void;
|
|
110
|
+
emitPrev(): void;
|
|
111
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SmartPlayerComponent, never>;
|
|
112
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SmartPlayerComponent, "ngx-smart-player", never, { "config": { "alias": "config"; "required": true; }; "plugins": { "alias": "plugins"; "required": false; }; "ports": { "alias": "ports"; "required": false; }; "engines": { "alias": "engines"; "required": false; }; "debug": { "alias": "debug"; "required": false; }; "startTimeSec": { "alias": "startTimeSec"; "required": false; }; "canPrev": { "alias": "canPrev"; "required": false; }; "canNext": { "alias": "canNext"; "required": false; }; "shuffleActive": { "alias": "shuffleActive"; "required": false; }; "shuffleLabel": { "alias": "shuffleLabel"; "required": false; }; "repeatMode": { "alias": "repeatMode"; "required": false; }; "repeatLabel": { "alias": "repeatLabel"; "required": false; }; "hasPlaylistControls": { "alias": "hasPlaylistControls"; "required": false; }; }, { "ended": "ended"; "videoSize": "videoSize"; "progress": "progress"; "prev": "prev"; "next": "next"; "toggleShuffle": "toggleShuffle"; "cycleRepeat": "cycleRepeat"; "subtitles": "subtitles"; }, never, never, true, never>;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
interface SmartPlayerProvidersConfig {
|
|
116
|
+
subtitleProviders?: SubtitleProvider[];
|
|
117
|
+
metadataProviders?: MetadataProvider[];
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Register provider implementations for subtitles/metadata.
|
|
121
|
+
* You can call this in your app `bootstrapApplication()` providers array.
|
|
122
|
+
*/
|
|
123
|
+
declare function provideSmartPlayer(cfg: SmartPlayerProvidersConfig): EnvironmentProviders;
|
|
124
|
+
|
|
125
|
+
export { SmartPlayerComponent, provideSmartPlayer };
|
|
126
|
+
export type { SmartPlayerProvidersConfig };
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ngxsplayer/ngx-smart-player",
|
|
3
|
+
"version": "0.0.1-next.0",
|
|
4
|
+
"description": "Angular smart video player with engine abstraction and plugin system (Shaka-based).",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"angular",
|
|
7
|
+
"video",
|
|
8
|
+
"player",
|
|
9
|
+
"shaka",
|
|
10
|
+
"drm",
|
|
11
|
+
"hls",
|
|
12
|
+
"dash"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"@angular/common": ">=16.0.0",
|
|
17
|
+
"@angular/core": ">=16.0.0",
|
|
18
|
+
"rxjs": ">=7.0.0",
|
|
19
|
+
"shaka-player": ">=5.0.2",
|
|
20
|
+
"@ngxsp/core": "0.0.1-next.0"
|
|
21
|
+
},
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"tslib": "^2.3.0"
|
|
25
|
+
},
|
|
26
|
+
"peerDependenciesMeta": {
|
|
27
|
+
"@ngxsp/core": {
|
|
28
|
+
"optional": false
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"module": "fesm2022/ngxsplayer-ngx-smart-player.mjs",
|
|
32
|
+
"typings": "index.d.ts",
|
|
33
|
+
"exports": {
|
|
34
|
+
"./package.json": {
|
|
35
|
+
"default": "./package.json"
|
|
36
|
+
},
|
|
37
|
+
".": {
|
|
38
|
+
"types": "./index.d.ts",
|
|
39
|
+
"default": "./fesm2022/ngxsplayer-ngx-smart-player.mjs"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|