diff --git a/apps/desktop/electron/media-protocol.test.ts b/apps/desktop/electron/media-protocol.test.ts index 684d129490..b54d65b1c8 100644 --- a/apps/desktop/electron/media-protocol.test.ts +++ b/apps/desktop/electron/media-protocol.test.ts @@ -164,6 +164,44 @@ describe('createMediaProtocolHandler', () => { expect(headers.get('range')).toBe('bytes=0-1023') }) + it('sends the connection extra gateway headers on remote media without clobbering range negotiation', async () => { + const deps = dependencies({ + resolveRemoteConnection: vi.fn(async () => ({ + authMode: 'token' as const, + baseUrl: 'https://gateway.test', + headers: { 'CF-Access-Client-Id': 'client-id', Range: 'bytes=9-9' }, + mode: 'remote' as const, + token: 'secret' + })) + }) + + await createMediaProtocolHandler(deps)( + request('hermes-media://remote/%2Ftmp%2Fclip.mp4', { Range: 'bytes=0-1023' }) + ) + + const [, headers] = vi.mocked(deps.fetchRemote).mock.calls[0] + expect(headers.get('cf-access-client-id')).toBe('client-id') + expect(headers.get('range')).toBe('bytes=0-1023') + expect(headers.get('x-hermes-session-token')).toBe('secret') + }) + + it('sends the connection extra gateway headers on OAuth cookie-session remote media', async () => { + const deps = dependencies({ + resolveRemoteConnection: vi.fn(async () => ({ + authMode: 'oauth' as const, + baseUrl: 'https://gateway.test', + headers: { 'CF-Access-Client-Id': 'client-id' }, + mode: 'remote' as const, + token: null + })) + }) + + await createMediaProtocolHandler(deps)(request('hermes-media://remote/%2Ftmp%2Fclip.mp4')) + + const [, headers] = vi.mocked(deps.fetchRemoteWithCookies).mock.calls[0] + expect(headers.get('cf-access-client-id')).toBe('client-id') + }) + it('adds profile scope when one registry backend serves multiple profiles', async () => { const deps = dependencies({ resolveRemoteConnection: vi.fn(async () => ({ diff --git a/apps/desktop/electron/media-protocol.ts b/apps/desktop/electron/media-protocol.ts index 463a622989..5a8eb76066 100644 --- a/apps/desktop/electron/media-protocol.ts +++ b/apps/desktop/electron/media-protocol.ts @@ -36,6 +36,7 @@ export interface MediaRemoteScope { export interface MediaRemoteConnection { authMode?: 'oauth' | 'token' baseUrl: string + headers?: Record mode?: 'local' | 'remote' token?: null | string sharedRemote?: boolean @@ -163,6 +164,14 @@ export function createMediaProtocolHandler(dependencies: MediaProtocolDependenci connection.sharedRemote ? target.profile : undefined ) + // The gateway's configured extra headers (access-proxy gates) travel on + // every remote request; forwarded range/cache negotiation headers win. + for (const [name, value] of Object.entries(connection.headers ?? {})) { + if (!headers.has(name)) { + headers.set(name, value) + } + } + if (connection.authMode === 'oauth') { return await requestWithOauthFallback(connection.baseUrl, { ensureNativeAccessToken: dependencies.ensureRemoteBearer,