mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-26 12:55:16 -07:00
fix(jellyfin): fix remote progress sync, seek reporting, and startup sto
- arm active playback before loadfile with loadedMediaPath: null to suppress premature stop events - force immediate progress report on seek-like position jumps at the mpv time-pos level - send positionTicks and failed=false in reportStopped payload - remove EventName from HTTP timeline payloads (websocket-only field) - add startup grace window to drop stop events before media finishes loading
This commit is contained in:
@@ -289,6 +289,44 @@ test('reportProgress posts timeline payload and treats failure as non-fatal', as
|
||||
assert.deepEqual(JSON.parse(String(timelineCall.init.body)), expectedPostedPayload);
|
||||
});
|
||||
|
||||
test('timeline payload omits websocket-only event names', () => {
|
||||
const payload = buildJellyfinTimelinePayload({
|
||||
itemId: 'movie-2',
|
||||
positionTicks: 123456,
|
||||
eventName: 'TimeUpdate',
|
||||
});
|
||||
|
||||
assert.equal('EventName' in payload, false);
|
||||
});
|
||||
|
||||
test('reportStopped posts final position and explicit non-failed state', async () => {
|
||||
const fetchCalls: Array<{ input: string; init: RequestInit }> = [];
|
||||
const service = new JellyfinRemoteSessionService({
|
||||
serverUrl: 'http://jellyfin.local',
|
||||
accessToken: 'token-stop-payload',
|
||||
deviceId: 'device-stop-payload',
|
||||
webSocketFactory: () => new FakeWebSocket() as unknown as any,
|
||||
fetchImpl: (async (input, init) => {
|
||||
fetchCalls.push({ input: String(input), init: init ?? {} });
|
||||
return new Response(null, { status: 200 });
|
||||
}) as typeof fetch,
|
||||
});
|
||||
|
||||
const ok = await service.reportStopped({
|
||||
itemId: 'movie-stop',
|
||||
positionTicks: 7654321,
|
||||
failed: false,
|
||||
});
|
||||
|
||||
const stoppedCall = fetchCalls.find((call) => call.input.endsWith('/Sessions/Playing/Stopped'));
|
||||
assert.equal(ok, true);
|
||||
assert.ok(stoppedCall);
|
||||
assert.ok(typeof stoppedCall.init.body === 'string');
|
||||
const posted = JSON.parse(String(stoppedCall.init.body));
|
||||
assert.equal(posted.PositionTicks, 7654321);
|
||||
assert.equal(posted.Failed, false);
|
||||
});
|
||||
|
||||
test('advertiseNow validates server registration using Sessions endpoint', async () => {
|
||||
const sockets: FakeWebSocket[] = [];
|
||||
const calls: string[] = [];
|
||||
|
||||
@@ -20,6 +20,7 @@ export interface JellyfinTimelinePlaybackState {
|
||||
subtitleStreamIndex?: number | null;
|
||||
playlistItemId?: string | null;
|
||||
eventName?: string;
|
||||
failed?: boolean;
|
||||
}
|
||||
|
||||
export interface JellyfinTimelinePayload {
|
||||
@@ -36,7 +37,7 @@ export interface JellyfinTimelinePayload {
|
||||
AudioStreamIndex?: number | null;
|
||||
SubtitleStreamIndex?: number | null;
|
||||
PlaylistItemId?: string | null;
|
||||
EventName: string;
|
||||
Failed?: boolean;
|
||||
}
|
||||
|
||||
interface JellyfinRemoteSocket {
|
||||
@@ -168,7 +169,7 @@ export function buildJellyfinTimelinePayload(
|
||||
AudioStreamIndex: asNullableInteger(state.audioStreamIndex),
|
||||
SubtitleStreamIndex: asNullableInteger(state.subtitleStreamIndex),
|
||||
PlaylistItemId: state.playlistItemId,
|
||||
EventName: state.eventName || 'timeupdate',
|
||||
Failed: state.failed,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -269,10 +270,7 @@ export class JellyfinRemoteSessionService {
|
||||
}
|
||||
|
||||
public async reportPlaying(state: JellyfinTimelinePlaybackState): Promise<boolean> {
|
||||
return this.postTimeline('/Sessions/Playing', {
|
||||
...buildJellyfinTimelinePayload(state),
|
||||
EventName: state.eventName || 'start',
|
||||
});
|
||||
return this.postTimeline('/Sessions/Playing', buildJellyfinTimelinePayload(state));
|
||||
}
|
||||
|
||||
public async reportProgress(state: JellyfinTimelinePlaybackState): Promise<boolean> {
|
||||
@@ -282,7 +280,7 @@ export class JellyfinRemoteSessionService {
|
||||
public async reportStopped(state: JellyfinTimelinePlaybackState): Promise<boolean> {
|
||||
return this.postTimeline('/Sessions/Playing/Stopped', {
|
||||
...buildJellyfinTimelinePayload(state),
|
||||
EventName: state.eventName || 'stop',
|
||||
Failed: state.failed === true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user