From 31d0c9322fe7f3c7195bf993c1a5566cd2df9f23 Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Wed, 6 May 2026 19:13:24 -0700 Subject: [PATCH] fix: Always respect the returned MIME type (#2473) - Closes #2449 --- src/camera-manager/frigate/media.ts | 10 ---- src/components/viewer/provider.ts | 32 ++++++------ src/ha/browse-media/item.ts | 4 -- src/utils/mime-type.ts | 19 +++++++ src/view/item.ts | 8 --- tests/camera-manager/frigate/media.test.ts | 33 ------------ .../reolink/engine-reolink.test.ts | 1 - tests/ha/browse-media/item.test.ts | 19 +------ tests/utils/mime-type.test.ts | 50 +++++++++++++++++++ tests/view/item.test.ts | 17 +------ 10 files changed, 88 insertions(+), 105 deletions(-) create mode 100644 src/utils/mime-type.ts create mode 100644 tests/utils/mime-type.test.ts diff --git a/src/camera-manager/frigate/media.ts b/src/camera-manager/frigate/media.ts index 32fe3f2b..81d56445 100644 --- a/src/camera-manager/frigate/media.ts +++ b/src/camera-manager/frigate/media.ts @@ -6,7 +6,6 @@ import { EventViewMedia, RecordingViewMedia, ReviewViewMedia, - VideoContentType, ViewMedia, ViewMediaType, } from '../../view/item'; @@ -60,9 +59,6 @@ export class FrigateEventViewMedia extends ViewMedia implements EventViewMedia { // progress. return !this.getEndTime(); } - public getVideoContentType(): VideoContentType | null { - return VideoContentType.HLS; - } public getID(): string { return this._event.id; } @@ -141,9 +137,6 @@ export class FrigateRecordingViewMedia extends ViewMedia implements RecordingVie // progress. return !this.getEndTime(); } - public getVideoContentType(): VideoContentType | null { - return VideoContentType.HLS; - } public getContentID(): string | null { return this._contentID; } @@ -186,9 +179,6 @@ export class FrigateReviewViewMedia extends ViewMedia implements ReviewViewMedia public inProgress(): boolean | null { return !this.getEndTime(); } - public getVideoContentType(): VideoContentType | null { - return VideoContentType.HLS; - } public getContentID(): string | null { return this._contentID; } diff --git a/src/components/viewer/provider.ts b/src/components/viewer/provider.ts index 860be540..2c5e6974 100644 --- a/src/components/viewer/provider.ts +++ b/src/components/viewer/provider.ts @@ -22,13 +22,14 @@ import { ViewerConfig } from '../../config/schema/viewer.js'; import { canonicalizeHAURL } from '../../ha/canonical-url.js'; import { isHARelativeURL } from '../../ha/is-ha-relative-url.js'; import { ResolvedMediaCache, resolveMedia } from '../../ha/resolved-media.js'; -import { HomeAssistant } from '../../ha/types.js'; +import { HomeAssistant, ResolvedMedia } from '../../ha/types.js'; import { localize } from '../../localize/localize.js'; import '../../patches/ha-hls-player.js'; import viewerProviderStyle from '../../scss/viewer-provider.scss'; import { MediaPlayer, MediaPlayerController, MediaPlayerElement } from '../../types.js'; +import { classifyMimeType } from '../../utils/mime-type.js'; import { ViewItemClassifier } from '../../view/item-classifier.js'; -import { VideoContentType, ViewMedia } from '../../view/item.js'; +import { ViewMedia } from '../../view/item.js'; import { UnifiedQueryTransformer } from '../../view/unified-query-transformer.js'; import '../image-player.js'; import { renderNotificationBlockFromText } from '../notification/block.js'; @@ -62,23 +63,23 @@ export class AdvancedCameraCardViewerProvider extends LitElement implements Medi private _refProvider: Ref = createRef(); private _lazyLoadController: LazyLoadController = new LazyLoadController(this); - private _resolvedMediaURL: string | null = null; + private _resolvedMedia: ResolvedMedia | null = null; private _signedURLController = new SignedURLController(this, () => { - if (!this.hass || !this._resolvedMediaURL) { + if (!this.hass || !this._resolvedMedia) { return {}; } // HA-relative URLs need no proxying or signing. - if (isHARelativeURL(this._resolvedMediaURL)) { + if (isHARelativeURL(this._resolvedMedia.url)) { return { - endpoint: { endpoint: canonicalizeHAURL(this.hass, this._resolvedMediaURL) }, + endpoint: { endpoint: canonicalizeHAURL(this.hass, this._resolvedMedia.url) }, }; } const cameraID = this.media?.getCameraID(); const camera = cameraID ? this.cameraManager?.getStore().getCamera(cameraID) : null; return { hass: this.hass, - endpoint: { endpoint: this._resolvedMediaURL }, + endpoint: { endpoint: this._resolvedMedia.url }, proxyConfig: camera?.getMediaProxyConfig(), }; }); @@ -128,19 +129,18 @@ export class AdvancedCameraCardViewerProvider extends LitElement implements Medi private async _resolveURL(): Promise { const contentID = this.media?.getContentID(); if (!contentID || !this.hass || !this._lazyLoadController?.isLoaded()) { - this._resolvedMediaURL = null; + this._resolvedMedia = null; return; } // Clear immediately so the SignedURLController doesn't see a stale URL // from the previous media item during the async gap. - this._resolvedMediaURL = null; + this._resolvedMedia = null; - const resolved = + this._resolvedMedia = this.resolvedMediaCache?.get(contentID) ?? - (await resolveMedia(this.hass, contentID, this.resolvedMediaCache)); - - this._resolvedMediaURL = resolved?.url ?? null; + (await resolveMedia(this.hass, contentID, this.resolvedMediaCache)) ?? + null; this.requestUpdate(); } @@ -251,9 +251,11 @@ export class AdvancedCameraCardViewerProvider extends LitElement implements Medi // Note: crossorigin="anonymous" is required on