impl media interactions recording
close #159 close #72 listen to HTMLMediaElement's play/pause events, and replay them by programmatically play and pause the target element.
This commit is contained in:
@@ -58,7 +58,7 @@
|
||||
"dependencies": {
|
||||
"@types/smoothscroll-polyfill": "^0.3.0",
|
||||
"mitt": "^1.1.3",
|
||||
"rrweb-snapshot": "^0.7.23",
|
||||
"rrweb-snapshot": "file:../snapshot",
|
||||
"smoothscroll-polyfill": "^0.4.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ function record(options: recordOptions = {}): listenerHandler | undefined {
|
||||
inlineStylesheet = true,
|
||||
maskAllInputs = false,
|
||||
hooks,
|
||||
mousemoveWait = 50
|
||||
mousemoveWait = 50,
|
||||
} = options;
|
||||
// runtime checks for user options
|
||||
if (!emit) {
|
||||
@@ -178,11 +178,21 @@ function record(options: recordOptions = {}): listenerHandler | undefined {
|
||||
},
|
||||
}),
|
||||
),
|
||||
mediaInteractionCb: p =>
|
||||
wrappedEmit(
|
||||
wrapEvent({
|
||||
type: EventType.IncrementalSnapshot,
|
||||
data: {
|
||||
source: IncrementalSource.MediaInteraction,
|
||||
...p,
|
||||
},
|
||||
}),
|
||||
),
|
||||
blockClass,
|
||||
ignoreClass,
|
||||
maskAllInputs,
|
||||
inlineStylesheet,
|
||||
mousemoveWait
|
||||
mousemoveWait,
|
||||
},
|
||||
hooks,
|
||||
),
|
||||
|
||||
@@ -31,6 +31,8 @@ import {
|
||||
IncrementalSource,
|
||||
hooksParam,
|
||||
Arguments,
|
||||
mediaInteractionCallback,
|
||||
MediaInteractions,
|
||||
} from '../types';
|
||||
import { deepDelete, isParentRemoved, isAncestorInSet } from './collection';
|
||||
|
||||
@@ -517,6 +519,26 @@ function initInputObserver(
|
||||
};
|
||||
}
|
||||
|
||||
function initMediaInteractionObserver(
|
||||
mediaInteractionCb: mediaInteractionCallback,
|
||||
blockClass: blockClass,
|
||||
): listenerHandler {
|
||||
const handler = (type: 'play' | 'pause') => (event: Event) => {
|
||||
const { target } = event;
|
||||
if (!target || isBlocked(target as Node, blockClass)) {
|
||||
return;
|
||||
}
|
||||
mediaInteractionCb({
|
||||
type: type === 'play' ? MediaInteractions.Play : MediaInteractions.Pause,
|
||||
id: mirror.getId(target as INode),
|
||||
});
|
||||
};
|
||||
const handlers = [on('play', handler('play')), on('pause', handler('pause'))];
|
||||
return () => {
|
||||
handlers.forEach(h => h());
|
||||
};
|
||||
}
|
||||
|
||||
function mergeHooks(o: observerParam, hooks: hooksParam) {
|
||||
const {
|
||||
mutationCb,
|
||||
@@ -525,6 +547,7 @@ function mergeHooks(o: observerParam, hooks: hooksParam) {
|
||||
scrollCb,
|
||||
viewportResizeCb,
|
||||
inputCb,
|
||||
mediaInteractionCb,
|
||||
} = o;
|
||||
o.mutationCb = (...p: Arguments<mutationCallBack>) => {
|
||||
if (hooks.mutation) {
|
||||
@@ -562,6 +585,12 @@ function mergeHooks(o: observerParam, hooks: hooksParam) {
|
||||
}
|
||||
inputCb(...p);
|
||||
};
|
||||
o.mediaInteractionCb = (...p: Arguments<mediaInteractionCallback>) => {
|
||||
if (hooks.mediaInteaction) {
|
||||
hooks.mediaInteaction(...p);
|
||||
}
|
||||
mediaInteractionCb(...p);
|
||||
};
|
||||
}
|
||||
|
||||
export default function initObservers(
|
||||
@@ -588,6 +617,10 @@ export default function initObservers(
|
||||
o.ignoreClass,
|
||||
o.maskAllInputs,
|
||||
);
|
||||
const mediaInteractionHandler = initMediaInteractionObserver(
|
||||
o.mediaInteractionCb,
|
||||
o.blockClass,
|
||||
);
|
||||
return () => {
|
||||
mutationObserver.disconnect();
|
||||
mousemoveHandler();
|
||||
@@ -595,5 +628,6 @@ export default function initObservers(
|
||||
scrollHandler();
|
||||
viewportResizeHandler();
|
||||
inputHandler();
|
||||
mediaInteractionHandler();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
ReplayerEvents,
|
||||
Handler,
|
||||
Emitter,
|
||||
MediaInteractions,
|
||||
} from '../types';
|
||||
import { mirror, polyfill } from '../utils';
|
||||
import getInjectStyleRules from './styles/inject-style';
|
||||
@@ -587,6 +588,26 @@ export class Replayer {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IncrementalSource.MediaInteraction: {
|
||||
const target = mirror.getNode(d.id);
|
||||
if (!target) {
|
||||
return this.debugNodeNotFound(d, d.id);
|
||||
}
|
||||
const mediaEl = (target as Node) as HTMLMediaElement;
|
||||
if (d.type === MediaInteractions.Pause) {
|
||||
mediaEl.pause();
|
||||
}
|
||||
if (d.type === MediaInteractions.Play) {
|
||||
if (mediaEl.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) {
|
||||
mediaEl.play();
|
||||
} else {
|
||||
mediaEl.addEventListener('canplay', () => {
|
||||
mediaEl.play();
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
22
src/types.ts
22
src/types.ts
@@ -60,6 +60,7 @@ export enum IncrementalSource {
|
||||
ViewportResize,
|
||||
Input,
|
||||
TouchMove,
|
||||
MediaInteraction,
|
||||
}
|
||||
|
||||
export type mutationData = {
|
||||
@@ -88,13 +89,18 @@ export type inputData = {
|
||||
id: number;
|
||||
} & inputValue;
|
||||
|
||||
export type mediaInteractionData = {
|
||||
source: IncrementalSource.MediaInteraction;
|
||||
} & mediaInteractionParam;
|
||||
|
||||
export type incrementalData =
|
||||
| mutationData
|
||||
| mousemoveData
|
||||
| mouseInteractionData
|
||||
| scrollData
|
||||
| viewportResizeData
|
||||
| inputData;
|
||||
| inputData
|
||||
| mediaInteractionData;
|
||||
|
||||
export type event =
|
||||
| domContentLoadedEvent
|
||||
@@ -130,6 +136,7 @@ export type observerParam = {
|
||||
scrollCb: scrollCallback;
|
||||
viewportResizeCb: viewportResizeCallback;
|
||||
inputCb: inputCallback;
|
||||
mediaInteractionCb: mediaInteractionCallback;
|
||||
blockClass: blockClass;
|
||||
ignoreClass: string;
|
||||
maskAllInputs: boolean;
|
||||
@@ -144,6 +151,7 @@ export type hooksParam = {
|
||||
scroll?: scrollCallback;
|
||||
viewportResize?: viewportResizeCallback;
|
||||
input?: inputCallback;
|
||||
mediaInteaction?: mediaInteractionCallback;
|
||||
};
|
||||
|
||||
export type textCursor = {
|
||||
@@ -245,6 +253,18 @@ export type inputValue = {
|
||||
|
||||
export type inputCallback = (v: inputValue & { id: number }) => void;
|
||||
|
||||
export const enum MediaInteractions {
|
||||
Play,
|
||||
Pause,
|
||||
}
|
||||
|
||||
export type mediaInteractionParam = {
|
||||
type: MediaInteractions;
|
||||
id: number;
|
||||
};
|
||||
|
||||
export type mediaInteractionCallback = (p: mediaInteractionParam) => void;
|
||||
|
||||
export type Mirror = {
|
||||
map: idNodeMap;
|
||||
getId: (n: INode) => number;
|
||||
|
||||
Reference in New Issue
Block a user