mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-29 18:46:59 +00:00
fix(desktop): stream remote media through gateway
This commit is contained in:
parent
3a1efa5e30
commit
3e47efeb25
3 changed files with 55 additions and 14 deletions
|
|
@ -27,8 +27,8 @@ import {
|
|||
mediaKind,
|
||||
mediaName,
|
||||
mediaPathFromMarkdownHref,
|
||||
mediaStreamUrl,
|
||||
resolveMediaDisplaySrc
|
||||
resolveMediaDisplaySrc,
|
||||
resolveMediaPlaybackSrc
|
||||
} from '@/lib/media'
|
||||
import { previewTargetFromMarkdownHref } from '@/lib/preview-targets'
|
||||
import { sessionRefFromMarkdownHref } from '@/lib/session-refs'
|
||||
|
|
@ -98,16 +98,6 @@ function preprocessWithTailRepair(text: string): string {
|
|||
}
|
||||
}
|
||||
|
||||
async function mediaSrc(path: string): Promise<string> {
|
||||
// Stream audio/video through the custom protocol: data URLs are capped and
|
||||
// load the whole file into memory, which broke playback for larger videos.
|
||||
if (window.hermesDesktop && ['audio', 'video'].includes(mediaKind(path))) {
|
||||
return mediaStreamUrl(path)
|
||||
}
|
||||
|
||||
return resolveMediaDisplaySrc(path)
|
||||
}
|
||||
|
||||
function useOpenMediaFile(path: string) {
|
||||
const [openFailed, setOpenFailed] = useState(false)
|
||||
|
||||
|
|
@ -170,7 +160,7 @@ function MediaAttachment({ path }: { path: string }) {
|
|||
}
|
||||
}
|
||||
|
||||
void mediaSrc(path)
|
||||
void resolveMediaPlaybackSrc(path)
|
||||
.then(value => {
|
||||
if (value.startsWith('blob:')) {
|
||||
objectUrl = value
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ import {
|
|||
isInlineMediaSrc,
|
||||
isRemoteGateway,
|
||||
mediaExternalUrl,
|
||||
resolveMediaDisplaySrc
|
||||
resolveMediaDisplaySrc,
|
||||
resolveMediaPlaybackSrc
|
||||
} from './media'
|
||||
|
||||
describe('isRemoteGateway', () => {
|
||||
|
|
@ -139,6 +140,40 @@ describe('resolveMediaDisplaySrc', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('resolveMediaPlaybackSrc', () => {
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals()
|
||||
$connection.set(null)
|
||||
})
|
||||
|
||||
it('keeps a remote HTTPS video URL unchanged', async () => {
|
||||
vi.stubGlobal('window', { hermesDesktop: { api: vi.fn() } })
|
||||
$connection.set({ mode: 'remote', baseUrl: 'https://gateway.test', token: 'secret' } as never)
|
||||
|
||||
await expect(resolveMediaPlaybackSrc('https://cdn.example.com/render.mp4')).resolves.toBe(
|
||||
'https://cdn.example.com/render.mp4'
|
||||
)
|
||||
})
|
||||
|
||||
it('routes gateway-local video through the authenticated download endpoint', async () => {
|
||||
vi.stubGlobal('window', { hermesDesktop: { api: vi.fn() } })
|
||||
$connection.set({ mode: 'remote', baseUrl: 'https://gateway.test', token: 's e/cret' } as never)
|
||||
|
||||
await expect(resolveMediaPlaybackSrc('/root/outputs/render.mp4')).resolves.toBe(
|
||||
'https://gateway.test/api/files/download?path=%2Froot%2Foutputs%2Frender.mp4&token=s%20e%2Fcret'
|
||||
)
|
||||
})
|
||||
|
||||
it('uses the Electron streaming protocol for local desktop video', async () => {
|
||||
vi.stubGlobal('window', { hermesDesktop: { api: vi.fn() } })
|
||||
$connection.set({ mode: 'local' } as never)
|
||||
|
||||
await expect(resolveMediaPlaybackSrc('C:\\renders\\demo.mp4')).resolves.toBe(
|
||||
'hermes-media://stream/C%3A%5Crenders%5Cdemo.mp4'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('gatewayMediaDataUrl', () => {
|
||||
const api = vi.fn(async ({ path }: { path: string }) => {
|
||||
if (path.startsWith('/api/fs/read-data-url?')) {
|
||||
|
|
|
|||
|
|
@ -82,6 +82,22 @@ export async function resolveMediaDisplaySrc(path: string): Promise<string> {
|
|||
return window.hermesDesktop.readFileDataUrl(filePathFromMediaPath(path))
|
||||
}
|
||||
|
||||
// Audio/video need a seekable source instead of a whole-file data URL. Keep
|
||||
// remote URLs untouched, route gateway-local files through the authenticated
|
||||
// download endpoint, and reserve the Electron protocol for files on this
|
||||
// desktop machine.
|
||||
export async function resolveMediaPlaybackSrc(path: string): Promise<string> {
|
||||
if (isInlineMediaSrc(path)) {
|
||||
return path
|
||||
}
|
||||
|
||||
if (window.hermesDesktop && ['audio', 'video'].includes(mediaKind(path))) {
|
||||
return isRemoteGateway() ? mediaExternalUrl(path) : mediaStreamUrl(path)
|
||||
}
|
||||
|
||||
return resolveMediaDisplaySrc(path)
|
||||
}
|
||||
|
||||
// Resolve a media path to a URL the shell can open. Remote mode rewrites
|
||||
// gateway-local paths to an authenticated /api/files/download URL (the file
|
||||
// lives on the gateway, not this disk); local mode keeps the file:// form.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue