@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
+ }