Skip to content
This repository was archived by the owner on Dec 17, 2024. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"main": "dist/index.js",
"unpkg": "dist/browser/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"test": "tsc --build tsconfig.test.json && browserify --debug --standalone TEST test/build/test/src/*.js -o test/build/browser/index.js",
"lint": "yarn lint:json && yarn lint:ts",
"lint:json": "prettier --list-different .eslintrc *.json",
"lint:ts": "eslint --ext .js,.ts --ignore-path .gitignore .",
Expand All @@ -16,13 +16,17 @@
"author": "Clockwork Dog <info@clockwork.dog>",
"license": "MIT",
"devDependencies": {
"@types/chai": "^4.3.0",
"@types/howler": "^2.2.4",
"@types/mocha": "^9.1.0",
"@typescript-eslint/eslint-plugin": "^4.12.0",
"@typescript-eslint/parser": "^4.12.0",
"browserify": "^17.0.0",
"chai": "^4.3.6",
"eslint": "^7.17.0",
"eslint-config-prettier": "^7.1.0",
"eslint-plugin-prettier": "^3.3.1",
"mocha": "^9.2.0",
"prettier": "^2.2.1",
"typescript": "^4.1.3"
},
Expand Down
108 changes: 65 additions & 43 deletions src/CogsConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import CogsClientMessage from './types/CogsClientMessage';
import { COGS_SERVER_PORT } from './helpers/urls';
import MediaClipStateMessage from './types/MediaClipStateMessage';
import AllMediaClipStatesMessage from './types/AllMediaClipStatesMessage';
import CogsMessage from './types/CogsMessage';

interface ConnectionEventListeners<
CustomTypes extends {
Expand All @@ -18,10 +19,19 @@ interface ConnectionEventListeners<
config: CustomTypes['config'];
updates: Partial<CustomTypes['inputPorts']>;
event: CustomTypes['inputEvents'] extends { [key: string]: EventValue | null } ? EventKeyValue<CustomTypes['inputEvents']> : Record<string, never>;
/**
* @ignore
*/
_cogsMessage: CogsMessage;
}

export type TimerState = Omit<Extract<CogsClientMessage, { type: 'adjustable_timer_update' }>, 'type'> & { startedAt: number };

export interface CogsConnectionOptions {
hostname?: string;
port?: number;
}

export default class CogsConnection<
CustomTypes extends {
config?: { [configKey: string]: ConfigValue };
Expand Down Expand Up @@ -60,7 +70,7 @@ export default class CogsConnection<
}

constructor(
{ hostname = document.location.hostname, port = COGS_SERVER_PORT }: { hostname?: string; port?: number } = {},
{ hostname = document.location.hostname, port = COGS_SERVER_PORT }: CogsConnectionOptions = {},
outputPortValues: CustomTypes['outputPorts'] = undefined
) {
this.currentOutputPortValues = { ...outputPortValues };
Expand All @@ -83,32 +93,8 @@ export default class CogsConnection<
this.websocket.onmessage = ({ data }) => {
try {
const parsed = JSON.parse(data);

try {
if (parsed.config) {
this.currentConfig = parsed.config;
this.dispatchEvent('config', this.currentConfig);
} else if (parsed.updates) {
this.currentInputPortValues = { ...this.currentInputPortValues, ...parsed.updates };
this.dispatchEvent('updates', this.currentInputPortValues);
} else if (parsed.event && parsed.event.key) {
this.dispatchEvent('event', parsed.event);
} else if (typeof parsed.message === 'object') {
switch (parsed.message.type) {
case 'adjustable_timer_update':
this._timerState = {
startedAt: Date.now(),
durationMillis: parsed.message.durationMillis,
ticking: parsed.message.ticking,
};
break;
case 'show_phase':
this._showPhase = parsed.message.phase;
break;
}

this.dispatchEvent('message', parsed.message);
}
this._handleCogsMessage(parsed);
} catch (e) {
console.warn('Error handling data', data, e);
}
Expand All @@ -118,6 +104,43 @@ export default class CogsConnection<
};
}

/**
* @ignore
*/
_handleCogsMessage(message: {
config?: CustomTypes['config'];
updates?: Partial<CustomTypes['inputPorts']>;
event?: CustomTypes['inputEvents'] extends { [key: string]: EventValue | null }
? EventKeyValue<CustomTypes['inputEvents']>
: Record<string, never>;
message?: CogsClientMessage;
}): void {
if (message.config) {
this.currentConfig = message.config;
this.dispatchEvent('config', this.currentConfig);
} else if (message.updates) {
this.currentInputPortValues = { ...this.currentInputPortValues, ...message.updates };
this.dispatchEvent('updates', this.currentInputPortValues);
} else if (message.event && message.event.key) {
this.dispatchEvent('event', message.event);
} else if (typeof message.message === 'object') {
switch (message.message.type) {
case 'adjustable_timer_update':
this._timerState = {
startedAt: Date.now(),
durationMillis: message.message.durationMillis,
ticking: message.message.ticking,
};
break;
case 'show_phase':
this._showPhase = message.message.phase;
break;
}

this.dispatchEvent('message', message.message);
}
}

public get isConnected(): boolean {
return this.websocket.readyState === WebSocket.OPEN;
}
Expand All @@ -126,39 +149,38 @@ export default class CogsConnection<
this.websocket.close();
}

private sendToCogs(message: CogsMessage) {
if (this.isConnected) {
this.websocket.send(JSON.stringify(message));
}
this.dispatchEvent('_cogsMessage', message);
}

public sendEvent<EventName extends keyof CustomTypes['outputEvents']>(
eventName: EventName,
...[eventValue]: CustomTypes['outputEvents'][EventName] extends null ? [] : [CustomTypes['outputEvents'][EventName]]
): void {
if (this.isConnected) {
this.websocket.send(
JSON.stringify({
event: {
key: eventName,
value: eventValue,
},
})
);
this.sendToCogs({
event: {
key: eventName as string,
value: eventValue as EventValue | undefined,
},
});
}
}

public setOutputPortValues(values: Partial<CustomTypes['outputPorts']>): void {
this.currentOutputPortValues = { ...this.currentOutputPortValues, ...values };
if (this.isConnected) {
this.websocket.send(JSON.stringify({ updates: values }));
}
this.sendToCogs({ updates: values as any });
}

sendInitialMediaClipStates(allMediaClipStates: AllMediaClipStatesMessage): void {
if (this.isConnected) {
this.websocket.send(JSON.stringify({ allMediaClipStates }));
}
this.sendToCogs({ allMediaClipStates });
}

sendMediaClipState(mediaClipState: MediaClipStateMessage): void {
if (this.isConnected) {
this.websocket.send(JSON.stringify({ mediaClipState }));
}
this.sendToCogs({ mediaClipState });
}

// Type-safe wrapper around EventTarget
Expand Down
13 changes: 13 additions & 0 deletions src/types/CogsMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import MediaClipStateMessage from './MediaClipStateMessage';
import AllMediaClipStatesMessage from './AllMediaClipStatesMessage';
import { EventValue, PortValue } from './valueTypes';

export default interface CogsMessage {
mediaClipState?: MediaClipStateMessage;
allMediaClipStates?: AllMediaClipStatesMessage;
event?: {
key: string;
value?: EventValue;
};
updates?: { [port: string]: PortValue };
}
1 change: 1 addition & 0 deletions test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build/
23 changes: 23 additions & 0 deletions test/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>cogs-client tests</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="../node_modules/mocha/mocha.css" />
</head>
<body>
<div id="mocha"></div>

<script src="../node_modules/mocha/mocha.js"></script>

<script class="mocha-init">
mocha.setup('bdd');
mocha.checkLeaks();
</script>
<script src="./build/browser/index.js"></script>
<script class="mocha-exec">
mocha.run();
</script>
</body>
</html>
45 changes: 45 additions & 0 deletions test/src/VideoPlayer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { expect } from 'chai';
import { CogsConnection } from '../../src';
import CogsMessage from '../../src/types/CogsMessage';
import VideoPlayer from '../../src/VideoPlayer';

describe('VideoPlayer', () => {
it('create', () => {
const videoPlayer = new VideoPlayer(new CogsConnection({ hostname: 'localhost', port: 0 }));
expect(videoPlayer).to.not.be.undefined;
});

it('play', () => {
const connection = new CogsConnection({ hostname: 'localhost', port: 0 });
const cogsMessages: CogsMessage[] = [];
connection.addEventListener('_cogsMessage', ({ detail }) => cogsMessages.push(detail));

const videoPlayer = new VideoPlayer(connection);
connection._handleCogsMessage({
message: {
type: 'video_play',
file: 'boom.mp4',
playId: 'video1',
fit: 'contain',
volume: 1,
},
});

waitForCogsMessage(
connection,
({ mediaClipState }) => mediaClipState?.mediaType === 'video' && mediaClipState.file === 'boom.mp4' && mediaClipState.playId === 'video1'
);
});
});

async function waitForCogsMessage(connection: CogsConnection, messagePredicate: (message: CogsMessage) => unknown) {
return new Promise<CogsMessage>((resolve) => {
const listener = ({ detail }: { detail: CogsMessage }) => {
if (messagePredicate(detail)) {
connection.removeEventListener('_cogsMessage', listener);
resolve(detail);
}
};
connection.addEventListener('_cogsMessage', listener);
});
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"sourceMap": true /* Generates corresponding '.map' file. */,
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "dist" /* Redirect output structure to the directory. */,
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
Expand Down
8 changes: 8 additions & 0 deletions tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "./tsconfig",
"include": ["test/src"],
"compilerOptions": {
"rootDir": "./",
"outDir": "./test/build"
}
}
Loading