mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 10:06:15 -08:00
* docs(playback): design recovery recommendations * docs(playback): plan recovery recommendations * refactor(playback): extract diagnostic utilities * feat(playback): define recovery recommendation contracts * feat(playback): rank recovery recommendations * feat(playback): track session recovery attempts * feat(playback): identify content recovery sessions * feat(ui): add ranked playback diagnostic panel * feat(playback): switch temporarily to recommended players * test(playback): cover temporary player recommendation * test(playback): verify recommendation capability guards * docs(playback): document recovery recommendations * fix(playback): keep recovery keys credential-free * fix(playback): remove derived tracking ownership * fix(playback): preserve distinct recovery fallbacks * fix(playback): reset resume for new sources * fix(playback): preserve desktop recovery guidance * docs(playback): clarify recovery policy exceptions * fix(playback): reject stale progress updates * fix(playback): keep protected recovery guidance neutral * test(playback): cover stale progress output * fix(playback): neutralize protected diagnostic copy * fix(playback): harden runtime guidance ownership * fix(playback): stabilize recovery application ownership * fix(ci): classify playback util coverage * fix(e2e): preserve playback fixture bytes
113 lines
5.0 KiB
HTML
113 lines
5.0 KiB
HTML
@for (application of renderedApplications(); track application.token) {
|
|
@if (application.binding; as binding) {
|
|
@if (binding.target === 'videojs') {
|
|
@defer (on immediate) {
|
|
@if (vjsOptions) {
|
|
<app-vjs-player
|
|
[options]="vjsOptions"
|
|
[mediaTitle]="resolvedMediaTitle()"
|
|
[volume]="volume()"
|
|
[showCaptions]="showCaptions()"
|
|
[interactionEnabled]="playbackInteractionEnabled()"
|
|
[startTime]="effectiveStartTime()"
|
|
[seriesNavigation]="seriesNavigation()"
|
|
(timeUpdate)="handleTimeUpdate($event, application)"
|
|
(playbackIssue)="handlePlaybackIssue($event, binding)"
|
|
(playbackEnded)="playbackEnded.emit()"
|
|
(previousEpisodeRequested)="
|
|
previousEpisodeRequested.emit()
|
|
"
|
|
(nextEpisodeRequested)="nextEpisodeRequested.emit()"
|
|
/>
|
|
}
|
|
} @placeholder {
|
|
<div
|
|
class="web-player-defer-placeholder"
|
|
aria-hidden="true"
|
|
></div>
|
|
}
|
|
} @else if (binding.target === 'html5') {
|
|
@defer (on immediate) {
|
|
@if (channel) {
|
|
<app-html-video-player
|
|
[channel]="$any(channel)"
|
|
[mediaTitle]="resolvedMediaTitle()"
|
|
[volume]="volume()"
|
|
[showCaptions]="showCaptions()"
|
|
[isLive]="resolvedIsLive()"
|
|
[interactionEnabled]="playbackInteractionEnabled()"
|
|
[startTime]="effectiveStartTime()"
|
|
[seriesNavigation]="seriesNavigation()"
|
|
(timeUpdate)="handleTimeUpdate($event, application)"
|
|
(playbackIssue)="handlePlaybackIssue($event, binding)"
|
|
(playbackEnded)="playbackEnded.emit()"
|
|
(previousEpisodeRequested)="
|
|
previousEpisodeRequested.emit()
|
|
"
|
|
(nextEpisodeRequested)="nextEpisodeRequested.emit()"
|
|
/>
|
|
}
|
|
} @placeholder {
|
|
<div
|
|
class="web-player-defer-placeholder"
|
|
aria-hidden="true"
|
|
></div>
|
|
}
|
|
} @else if (binding.target === 'artplayer') {
|
|
@defer (on immediate) {
|
|
@if (channel) {
|
|
<app-art-player
|
|
[channel]="$any(channel)"
|
|
[mediaTitle]="resolvedMediaTitle()"
|
|
[volume]="volume()"
|
|
[showCaptions]="showCaptions()"
|
|
[isLive]="resolvedIsLive()"
|
|
[interactionEnabled]="playbackInteractionEnabled()"
|
|
[startTime]="effectiveStartTime()"
|
|
[seriesNavigation]="seriesNavigation()"
|
|
(timeUpdate)="handleTimeUpdate($event, application)"
|
|
(playbackIssue)="handlePlaybackIssue($event, binding)"
|
|
(playbackEnded)="playbackEnded.emit()"
|
|
(previousEpisodeRequested)="
|
|
previousEpisodeRequested.emit()
|
|
"
|
|
(nextEpisodeRequested)="nextEpisodeRequested.emit()"
|
|
/>
|
|
}
|
|
} @placeholder {
|
|
<div
|
|
class="web-player-defer-placeholder"
|
|
aria-hidden="true"
|
|
></div>
|
|
}
|
|
}
|
|
} @else if (application.embeddedMpv) {
|
|
<app-embedded-mpv-player
|
|
[playback]="resolvedPlayback()"
|
|
[mediaTitle]="resolvedMediaTitle()"
|
|
[recordingFolder]="recordingFolder()"
|
|
[seriesNavigation]="seriesNavigation()"
|
|
(timeUpdate)="handleTimeUpdate($event, application)"
|
|
(playbackEnded)="playbackEnded.emit()"
|
|
(previousEpisodeRequested)="previousEpisodeRequested.emit()"
|
|
(nextEpisodeRequested)="nextEpisodeRequested.emit()"
|
|
/>
|
|
}
|
|
}
|
|
|
|
@if (visiblePlaybackDiagnostic(); as issue) {
|
|
<app-playback-diagnostic-panel
|
|
[diagnostic]="issue"
|
|
[recommendations]="recommendations()"
|
|
[playback]="resolvedPlayback()"
|
|
[supportsManagedExternalPlayers]="supportsManagedExternalPlayers"
|
|
[playbackExternallyTransferable]="playbackExternallyTransferable()"
|
|
[alternativeSources]="alternativeSources()"
|
|
[pending]="recoveryPending()"
|
|
(retryRequested)="retryPlayback()"
|
|
(playerRequested)="requestRecommendedPlayer($event)"
|
|
(alternativeSourceRequested)="alternativeSourceRequested.emit($event)"
|
|
(sourceCheckRequested)="sourceCheckRequested.emit($event)"
|
|
/>
|
|
}
|