mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 10:06:15 -08:00
feat(stalker): recover typed playlist requests
This commit is contained in:
1 parent
758c1cb77a
commit
2125e5a907
2 files changed
+70
-39
No files matched your search
@@ -434,6 +434,33 @@ describe('StalkerSessionService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the typed create-link payload without dropping its opaque playback context', async () => {
|
||||
bridge.stalkerSessionOpen.mockResolvedValue(READY);
|
||||
bridge.stalkerSessionRequest.mockResolvedValue({
|
||||
kind: 'success',
|
||||
operation: STALKER_SESSION_APPLICATION_OPERATIONS.CreateLink,
|
||||
payload: {
|
||||
playbackContextRef: 'opaque-playback-context-1',
|
||||
streamUrl: 'https://stream.example/live/1',
|
||||
},
|
||||
requestId: 'request-create-link-1',
|
||||
});
|
||||
|
||||
await expect(
|
||||
service.requestForPlaylist(
|
||||
PLAYLIST,
|
||||
STALKER_SESSION_APPLICATION_OPERATIONS.CreateLink,
|
||||
{
|
||||
command: 'ffmpeg https://portal.example/live/1',
|
||||
contentType: 'itv',
|
||||
}
|
||||
)
|
||||
).resolves.toEqual({
|
||||
playbackContextRef: 'opaque-playback-context-1',
|
||||
streamUrl: 'https://stream.example/live/1',
|
||||
});
|
||||
});
|
||||
|
||||
it('joins concurrent route recovery while preserving one retry per original operation', async () => {
|
||||
const transition: StalkerSessionRequestOutcome = {
|
||||
kind: 'failure',
|
||||
|
||||
@@ -10,11 +10,12 @@ import {
|
||||
type StalkerSessionControlOutcome,
|
||||
type StalkerSessionControlRequest,
|
||||
type StalkerSessionLeaseRef,
|
||||
type StalkerSessionOperationParameters,
|
||||
type StalkerSessionOperationResult,
|
||||
type StalkerSessionRequest,
|
||||
type StalkerSessionRequestOutcome,
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import {
|
||||
type StalkerAdaptedRequest,
|
||||
adaptLegacyStalkerRequest,
|
||||
mapStalkerSessionResultToLegacyResponse,
|
||||
} from './stalker-request-adapter';
|
||||
@@ -192,6 +193,40 @@ export class StalkerSessionService {
|
||||
return this.requireBridge().stalkerSessionRequest(request);
|
||||
}
|
||||
|
||||
async requestForPlaylist<
|
||||
Operation extends StalkerSessionApplicationOperation,
|
||||
>(
|
||||
playlist: Playlist,
|
||||
operation: Operation,
|
||||
parameters: StalkerSessionOperationParameters<Operation>
|
||||
): Promise<StalkerSessionOperationResult<Operation>> {
|
||||
let leaseRef = await this.resolveLease(playlist);
|
||||
let outcome = await this.request({
|
||||
leaseRef,
|
||||
operation,
|
||||
parameters,
|
||||
} as StalkerSessionRequest<Operation>);
|
||||
if (
|
||||
outcome.kind !== 'success' &&
|
||||
this.isRecoverable(outcome) &&
|
||||
(await this.recover(playlist, outcome, 'request'))
|
||||
) {
|
||||
const recoveredLease = this.getLeaseRef(playlist._id);
|
||||
if (recoveredLease !== undefined) {
|
||||
leaseRef = recoveredLease;
|
||||
outcome = await this.request({
|
||||
leaseRef,
|
||||
operation,
|
||||
parameters,
|
||||
} as StalkerSessionRequest<Operation>);
|
||||
}
|
||||
}
|
||||
if (outcome.kind !== 'success') {
|
||||
throw new StalkerSessionOutcomeError(outcome);
|
||||
}
|
||||
return outcome.payload;
|
||||
}
|
||||
|
||||
async control(
|
||||
request: StalkerSessionControlRequest
|
||||
): Promise<StalkerSessionControlOutcome> {
|
||||
@@ -293,13 +328,15 @@ export class StalkerSessionService {
|
||||
parameters: Readonly<Record<string, string | number>>
|
||||
): Promise<T> {
|
||||
const adapted = adaptLegacyStalkerRequest(parameters);
|
||||
const leaseRef = await this.resolveLease(playlist);
|
||||
const result = await this.executeAdaptedRequest(
|
||||
const result = await this.requestForPlaylist(
|
||||
playlist,
|
||||
leaseRef,
|
||||
adapted
|
||||
adapted.operation,
|
||||
adapted.parameters
|
||||
);
|
||||
return result as T;
|
||||
return mapStalkerSessionResultToLegacyResponse(
|
||||
adapted.operation,
|
||||
result
|
||||
) as T;
|
||||
}
|
||||
|
||||
private async resolveLease(
|
||||
@@ -349,39 +386,6 @@ export class StalkerSessionService {
|
||||
});
|
||||
}
|
||||
|
||||
private async executeAdaptedRequest(
|
||||
playlist: Playlist,
|
||||
leaseRef: StalkerSessionLeaseRef,
|
||||
adaptedRequest: StalkerAdaptedRequest
|
||||
): Promise<unknown> {
|
||||
let outcome = await this.request({
|
||||
leaseRef,
|
||||
operation: adaptedRequest.operation,
|
||||
parameters: adaptedRequest.parameters,
|
||||
} as StalkerSessionRequest);
|
||||
if (
|
||||
outcome.kind !== 'success' &&
|
||||
this.isRecoverable(outcome) &&
|
||||
(await this.recover(playlist, outcome, 'request'))
|
||||
) {
|
||||
const recoveredLease = this.getLeaseRef(playlist._id);
|
||||
if (recoveredLease !== undefined) {
|
||||
outcome = await this.request({
|
||||
leaseRef: recoveredLease,
|
||||
operation: adaptedRequest.operation,
|
||||
parameters: adaptedRequest.parameters,
|
||||
} as StalkerSessionRequest);
|
||||
}
|
||||
}
|
||||
if (outcome.kind !== 'success') {
|
||||
throw new StalkerSessionOutcomeError(outcome);
|
||||
}
|
||||
return mapStalkerSessionResultToLegacyResponse(
|
||||
outcome.operation,
|
||||
outcome.payload
|
||||
);
|
||||
}
|
||||
|
||||
private recover(
|
||||
playlist: Playlist,
|
||||
outcome: StalkerSessionNonSuccessOutcome,
|
||||
|
||||
Reference in new issue
Block a user