mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 02:46:16 -08:00
fix(playback): reveal capped external fallback
This commit is contained in:
1 parent
3bbb35ba30
commit
9e6c67ff48
2 files changed
+116
-2
No files matched your search
@@ -94,6 +94,52 @@ describe('createWebPlayerRecommendations external outcomes', () => {
|
||||
expect(targets(result)).not.toContain(InlinePlaybackPlayer.Html5);
|
||||
});
|
||||
|
||||
it('keeps a failed external target and reveals its capped sibling', () => {
|
||||
const result = recommendations({
|
||||
diagnostic: {
|
||||
...DIAGNOSTIC,
|
||||
code: PlaybackDiagnosticCode.DrmOrEncryption,
|
||||
source: PlaybackDiagnosticSource.Hls,
|
||||
container: 'm3u8',
|
||||
mimeType: 'application/vnd.apple.mpegurl',
|
||||
},
|
||||
attemptedTargets: new Set(['mpv']),
|
||||
externalStates: externalStates({
|
||||
mpv: { attempts: 1, sessionId: null, status: 'error' },
|
||||
}),
|
||||
});
|
||||
|
||||
expect(targets(result)).toEqual([
|
||||
InlinePlaybackPlayer.Html5,
|
||||
'vlc',
|
||||
'mpv',
|
||||
]);
|
||||
expect(result).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('retains a previously attempted target that the initial cap omitted', () => {
|
||||
const result = recommendations({
|
||||
diagnostic: {
|
||||
...DIAGNOSTIC,
|
||||
code: PlaybackDiagnosticCode.DrmOrEncryption,
|
||||
source: PlaybackDiagnosticSource.Hls,
|
||||
container: 'm3u8',
|
||||
mimeType: 'application/vnd.apple.mpegurl',
|
||||
},
|
||||
attemptedTargets: new Set(['vlc']),
|
||||
externalStates: externalStates({
|
||||
vlc: { attempts: 1, sessionId: null, status: 'error' },
|
||||
}),
|
||||
});
|
||||
|
||||
expect(targets(result)).toEqual([
|
||||
InlinePlaybackPlayer.Html5,
|
||||
'mpv',
|
||||
'vlc',
|
||||
]);
|
||||
expect(result).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('preserves policy order for equal attempt counts and stays capped at three', () => {
|
||||
const result = recommendations({
|
||||
attemptedTargets: new Set(['mpv', 'vlc']),
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
resolvePlaybackSourceKind,
|
||||
type PlaybackDiagnostic,
|
||||
type PlaybackRecommendation,
|
||||
type PlaybackRecommendationContext,
|
||||
type PlaybackRecommendationTarget,
|
||||
} from '@iptvnator/playback/util';
|
||||
import {
|
||||
@@ -34,7 +35,7 @@ export function createWebPlayerRecommendations(options: {
|
||||
(target) => target !== 'mpv' && target !== 'vlc'
|
||||
)
|
||||
);
|
||||
const recommendations = recommendPlaybackRecovery({
|
||||
const context: PlaybackRecommendationContext = {
|
||||
diagnostic: options.diagnostic,
|
||||
activeTarget: options.binding.target,
|
||||
attemptedTargets: attemptedInlineTargets,
|
||||
@@ -52,13 +53,69 @@ export function createWebPlayerRecommendations(options: {
|
||||
externalTransferable: options.playbackExternallyTransferable,
|
||||
},
|
||||
alternativeSourceCount: options.alternativeSourceCount,
|
||||
});
|
||||
};
|
||||
const recommendations = revealCappedExternalSiblings(
|
||||
recommendPlaybackRecovery(context),
|
||||
context,
|
||||
options.externalStates
|
||||
);
|
||||
return rerankExternalRecommendations(
|
||||
recommendations,
|
||||
options.externalStates
|
||||
);
|
||||
}
|
||||
|
||||
function revealCappedExternalSiblings(
|
||||
recommendations: readonly PlaybackRecommendation[],
|
||||
context: PlaybackRecommendationContext,
|
||||
states: ExternalRecoveryStates
|
||||
): readonly PlaybackRecommendation[] {
|
||||
const externalTargets: readonly ExternalPlayerName[] = ['mpv', 'vlc'];
|
||||
if (!externalTargets.some((target) => states[target].attempts > 0)) {
|
||||
return recommendations;
|
||||
}
|
||||
const retained = [...recommendations];
|
||||
for (const target of externalTargets) {
|
||||
if (
|
||||
retained.some(
|
||||
(recommendation) =>
|
||||
isExternalRecommendation(recommendation) &&
|
||||
recommendation.target === target
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const siblingTarget: ExternalPlayerName =
|
||||
target === 'mpv' ? 'vlc' : 'mpv';
|
||||
const candidate = recommendPlaybackRecovery({
|
||||
...context,
|
||||
attemptedTargets: new Set([
|
||||
...context.attemptedTargets,
|
||||
siblingTarget,
|
||||
]),
|
||||
}).find(
|
||||
(recommendation) =>
|
||||
isExternalRecommendation(recommendation) &&
|
||||
recommendation.target === target
|
||||
);
|
||||
if (!candidate) {
|
||||
continue;
|
||||
}
|
||||
if (retained.length >= 3) {
|
||||
const replaceIndex = retained.findLastIndex(
|
||||
(recommendation, index) =>
|
||||
index > 0 && !isExternalRecommendation(recommendation)
|
||||
);
|
||||
if (replaceIndex < 0) {
|
||||
continue;
|
||||
}
|
||||
retained.splice(replaceIndex, 1);
|
||||
}
|
||||
retained.push(candidate);
|
||||
}
|
||||
return retained;
|
||||
}
|
||||
|
||||
function rerankExternalRecommendations(
|
||||
recommendations: readonly PlaybackRecommendation[],
|
||||
states: ExternalRecoveryStates
|
||||
@@ -99,6 +156,17 @@ function rerankExternalRecommendations(
|
||||
});
|
||||
}
|
||||
|
||||
function isExternalRecommendation(
|
||||
recommendation: PlaybackRecommendation
|
||||
): recommendation is Extract<PlaybackRecommendation, { readonly action: 'player' }> & {
|
||||
readonly target: ExternalPlayerName;
|
||||
} {
|
||||
return (
|
||||
recommendation.action === 'player' &&
|
||||
(recommendation.target === 'mpv' || recommendation.target === 'vlc')
|
||||
);
|
||||
}
|
||||
|
||||
export function isPlaybackExternallyTransferable(
|
||||
playback: ResolvedPortalPlayback
|
||||
): boolean {
|
||||
|
||||
Reference in new issue
Block a user