const STREAMABLE_MEDIA_EXTENSIONS = [ '.avi', '.flac', '.m4a', '.mkv', '.mov', '.mp3', '.mp4', '.ogg', '.opus', '.wav', '.webm' ] as const const FORWARDED_MEDIA_REQUEST_HEADERS = ['accept', 'if-modified-since', 'if-none-match', 'if-range', 'range'] as const export const MEDIA_PROTOCOL = 'hermes-media' type MediaProtocolMode = 'remote' | 'stream' interface MediaProtocolTarget { connectionId?: string filePath: string mode: MediaProtocolMode profile?: string } export interface MediaRemoteScope { connectionId?: string profile?: string } export interface MediaRemoteConnection { authMode?: 'oauth' | 'token' baseUrl: string mode?: 'local' | 'remote' token?: null | string sharedRemote?: boolean } type MediaRequestMethod = 'GET' | 'HEAD' export interface MediaProtocolDependencies { ensureRemoteBearer: (baseUrl: string) => Promise fetchLocal: (resolvedPath: string, headers: Headers, method: MediaRequestMethod) => Promise fetchRemote: (url: string, headers: Headers, method: MediaRequestMethod) => Promise fetchRemoteWithCookies: (url: string, headers: Headers, method: MediaRequestMethod) => Promise resolveLocalFile: (filePath: string) => Promise resolveRemoteConnection: (scope: MediaRemoteScope) => Promise } function parseMediaProtocolTarget(rawUrl: string): MediaProtocolTarget { const url = new URL(rawUrl) const mode = url.hostname as MediaProtocolMode if (mode !== 'remote' && mode !== 'stream') { throw new Error('Unsupported media protocol target') } const filePath = decodeURIComponent(url.pathname.replace(/^\/+/, '')) if (!filePath) { throw new Error('Missing media path') } const connectionId = url.searchParams.get('connectionId')?.trim() || undefined const profile = url.searchParams.get('profile')?.trim() || undefined return { connectionId, filePath, mode, profile } } export function isStreamableMediaPath(filePath: string): boolean { const lower = filePath.toLowerCase() return STREAMABLE_MEDIA_EXTENSIONS.some(extension => lower.endsWith(extension)) } export function mediaRequestHeaders(source: Headers): Headers { const forwarded = new Headers() for (const name of FORWARDED_MEDIA_REQUEST_HEADERS) { const value = source.get(name) if (value) { forwarded.set(name, value) } } return forwarded } export function remoteMediaEndpoint(baseUrl: string, filePath: string, profile?: string): string { const normalizedBase = baseUrl.replace(/\/+$/, '') const url = new URL(`${normalizedBase}/api/files/stream`) if (url.protocol !== 'http:' && url.protocol !== 'https:') { throw new Error(`Unsupported Hermes backend URL protocol: ${url.protocol}`) } url.searchParams.set('path', filePath) if (profile) { url.searchParams.set('profile', profile) } return url.toString() } export function createMediaProtocolHandler(dependencies: MediaProtocolDependencies) { return async (request: Pick): Promise => { if (request.method !== 'GET' && request.method !== 'HEAD') { return new Response('Method not allowed', { headers: { allow: 'GET, HEAD' }, status: 405 }) } const method: MediaRequestMethod = request.method let target: MediaProtocolTarget try { target = parseMediaProtocolTarget(request.url) } catch { return new Response('Media not found', { status: 404 }) } if (!isStreamableMediaPath(target.filePath)) { return new Response('Unsupported media type', { status: 415 }) } const headers = mediaRequestHeaders(request.headers) if (target.mode === 'stream') { try { const resolvedPath = await dependencies.resolveLocalFile(target.filePath) if (!isStreamableMediaPath(resolvedPath)) { return new Response('Unsupported media type', { status: 415 }) } return await dependencies.fetchLocal(resolvedPath, headers, method) } catch { return new Response('Media not found', { status: 404 }) } } try { const connection = await dependencies.resolveRemoteConnection({ connectionId: target.connectionId, profile: target.profile }) if (connection.mode !== 'remote') { return new Response('Remote media backend unavailable', { status: 404 }) } const endpoint = remoteMediaEndpoint( connection.baseUrl, target.filePath, connection.sharedRemote ? target.profile : undefined ) if (connection.authMode === 'oauth') { const bearer = await dependencies.ensureRemoteBearer(connection.baseUrl) if (bearer) { headers.set('authorization', `Bearer ${bearer}`) return await dependencies.fetchRemote(endpoint, headers, method) } return await dependencies.fetchRemoteWithCookies(endpoint, headers, method) } if (!connection.token) { return new Response('Remote media authentication unavailable', { status: 401 }) } headers.set('x-hermes-session-token', connection.token) return await dependencies.fetchRemote(endpoint, headers, method) } catch { return new Response('Remote media unavailable', { status: 502 }) } } }