mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
fix(react): thumbnails broken when using hls media (#1210)
This commit is contained in:
@@ -21,10 +21,7 @@ export interface MediaProxy {
|
||||
*
|
||||
* The `get`, `set`, and `call` methods can be overridden to provide catch-all custom behavior.
|
||||
*/
|
||||
export const ProxyMixin = <T extends EventTarget>(
|
||||
PrimaryClass: AnyConstructor<T>,
|
||||
...AdditionalClasses: AnyConstructor<EventTarget>[]
|
||||
) => {
|
||||
export const ProxyMixin = <T extends EventTarget>(BaseClass: AnyConstructor<T>) => {
|
||||
class MediaProxyImpl extends EventTarget {
|
||||
#target: EventTarget | null = null;
|
||||
#types = new Set<string>();
|
||||
@@ -81,8 +78,12 @@ export const ProxyMixin = <T extends EventTarget>(
|
||||
};
|
||||
}
|
||||
|
||||
for (const Class of [PrimaryClass, ...AdditionalClasses]) {
|
||||
defineClassPropHooks(MediaProxyImpl, Class.prototype);
|
||||
for (
|
||||
let proto = BaseClass.prototype;
|
||||
proto && !Object.prototype.isPrototypeOf.call(proto, MediaProxyImpl.prototype);
|
||||
proto = Object.getPrototypeOf(proto)
|
||||
) {
|
||||
defineClassPropHooks(MediaProxyImpl, proto);
|
||||
}
|
||||
|
||||
return MediaProxyImpl as unknown as Constructor<T & MediaProxy>;
|
||||
|
||||
@@ -155,6 +155,46 @@ describe('ProxyMixin', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('prototype chain walking', () => {
|
||||
it('proxies methods inherited from ancestor prototypes', () => {
|
||||
class Grandparent extends EventTarget {
|
||||
inherited() {
|
||||
return 'grandparent';
|
||||
}
|
||||
}
|
||||
|
||||
class Parent extends Grandparent {
|
||||
direct() {
|
||||
return 'parent';
|
||||
}
|
||||
}
|
||||
|
||||
const ParentProxy = ProxyMixin(Parent);
|
||||
const proxy = new ParentProxy();
|
||||
const target = new Parent();
|
||||
proxy.attach(target);
|
||||
|
||||
expect(proxy.direct()).toBe('parent');
|
||||
expect(proxy.inherited()).toBe('grandparent');
|
||||
});
|
||||
|
||||
it('stops before prototypes the proxy already extends', () => {
|
||||
class Child extends EventTarget {
|
||||
custom() {
|
||||
return 'custom';
|
||||
}
|
||||
}
|
||||
|
||||
const ChildProxy = ProxyMixin(Child);
|
||||
|
||||
// addEventListener is defined by the proxy itself — the walk
|
||||
// should not overwrite it with a forwarding hook.
|
||||
expect(Object.getOwnPropertyDescriptor(ChildProxy.prototype, 'addEventListener')?.value).toBe(
|
||||
ChildProxy.prototype.addEventListener
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('EventListenerObject support', () => {
|
||||
it('invokes handleEvent on an object listener', () => {
|
||||
const { proxy, target } = setup();
|
||||
|
||||
@@ -372,7 +372,6 @@ export function CustomMediaMixin<T extends Constructor<HTMLElement>>(
|
||||
this.#childObserver?.observe(el, { attributes: true });
|
||||
}
|
||||
this.target?.append(clone);
|
||||
this.#enableDefaultTrack(clone as HTMLTrackElement);
|
||||
});
|
||||
|
||||
removeNativeChildren.forEach((clone, el) => {
|
||||
@@ -388,25 +387,11 @@ export function CustomMediaMixin<T extends Constructor<HTMLElement>>(
|
||||
const clone = this.#childMap.get(target as MediaChild);
|
||||
if (clone && attributeName) {
|
||||
clone.setAttribute(attributeName, (target as MediaChild).getAttribute(attributeName) ?? '');
|
||||
this.#enableDefaultTrack(clone as HTMLTrackElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#enableDefaultTrack(trackEl: HTMLTrackElement): void {
|
||||
// Enable default text tracks for chapters or metadata
|
||||
if (
|
||||
trackEl &&
|
||||
trackEl.localName === 'track' &&
|
||||
trackEl.default &&
|
||||
(trackEl.kind === 'chapters' || trackEl.kind === 'metadata') &&
|
||||
trackEl.track.mode === 'disabled'
|
||||
) {
|
||||
trackEl.track.mode = 'hidden';
|
||||
}
|
||||
}
|
||||
|
||||
#upgradeProperty(this: typeof nativeElTest, prop: keyof typeof nativeElTest) {
|
||||
// Sets properties that are set before the custom element is upgraded.
|
||||
// https://web.dev/custom-elements-best-practices/#make-properties-lazy
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import { ProxyMixin } from '../../core/media/proxy';
|
||||
|
||||
export const VideoProxy = ProxyMixin(
|
||||
globalThis.HTMLVideoElement ?? class {},
|
||||
globalThis.HTMLMediaElement ?? class {},
|
||||
globalThis.EventTarget ?? class {}
|
||||
);
|
||||
export const VideoProxy = ProxyMixin(globalThis.HTMLVideoElement ?? class {});
|
||||
|
||||
@@ -60,6 +60,11 @@ export const textTrackFeature = definePlayerFeature({
|
||||
}
|
||||
}
|
||||
|
||||
// Browsers don't auto-load cues for metadata/chapters tracks even with
|
||||
// the `default` attribute — mode stays 'disabled' until explicitly set.
|
||||
if (chaptersTrack && chaptersTrack.mode === 'disabled') chaptersTrack.mode = 'hidden';
|
||||
if (thumbnailTrack && thumbnailTrack.mode === 'disabled') thumbnailTrack.mode = 'hidden';
|
||||
|
||||
// VTTCue extends TextTrackCue with `text` — cast via `unknown` since
|
||||
// the CueList is typed as TextTrackCue which doesn't expose `text`.
|
||||
const chaptersCues: MediaTextCue[] = chaptersTrack?.cues
|
||||
|
||||
@@ -55,8 +55,9 @@ export const Thumbnail = forwardRef<HTMLDivElement, ThumbnailProps>(function Thu
|
||||
// Resolve thumbnails: external prop takes priority over auto <track> path.
|
||||
const thumbnails = useMemo(() => {
|
||||
if (externalThumbnails && externalThumbnails.length > 0) return externalThumbnails;
|
||||
if (!textTrack?.thumbnailCues.length) return [];
|
||||
return mapCuesToThumbnails(textTrack.thumbnailCues, textTrack.thumbnailTrackSrc ?? undefined);
|
||||
return textTrack && textTrack.thumbnailCues.length > 0
|
||||
? mapCuesToThumbnails(textTrack.thumbnailCues, textTrack.thumbnailTrackSrc ?? undefined)
|
||||
: [];
|
||||
}, [externalThumbnails, textTrack]);
|
||||
|
||||
const thumbnail = useMemo(() => core.findActiveThumbnail(thumbnails, time), [core, thumbnails, time]);
|
||||
|
||||
Reference in New Issue
Block a user