@clockworkdog/cogs-client 3.1.3 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AudioPlayer.d.ts +49 -0
- package/dist/AudioPlayer.js +484 -0
- package/dist/RtspStreamer.d.ts +27 -0
- package/dist/RtspStreamer.js +101 -0
- package/dist/VideoPlayer.d.ts +49 -0
- package/dist/VideoPlayer.js +394 -0
- package/dist/browser/index.mjs +17 -16
- package/dist/browser/index.umd.js +2 -2
- package/dist/helpers/urls.d.ts +6 -0
- package/dist/helpers/urls.js +19 -0
- package/dist/types/AllMediaClipStatesMessage.d.ts +5 -0
- package/dist/types/AllMediaClipStatesMessage.js +2 -0
- package/dist/types/AudioState.d.ts +39 -0
- package/dist/types/AudioState.js +2 -0
- package/dist/types/MediaClipStateMessage.d.ts +7 -0
- package/dist/types/MediaClipStateMessage.js +2 -0
- package/dist/types/VideoState.d.ts +26 -0
- package/dist/types/VideoState.js +8 -0
- package/dist/utils/NetworkHostPattern.d.ts +2 -1
- package/dist/utils/NetworkHostPattern.js +3 -2
- package/dist/utils/getPluginManifestErrors.js +1 -1
- package/dist/utils/setDate.d.ts +4 -0
- package/dist/utils/setDate.js +26 -0
- package/dist/utils/timesync.d.ts +28 -0
- package/dist/utils/timesync.js +67 -0
- package/package.json +2 -2
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.COGS_SERVER_PORT = void 0;
|
|
4
|
+
exports.assetUrl = assetUrl;
|
|
5
|
+
exports.preloadUrl = preloadUrl;
|
|
6
|
+
exports.COGS_SERVER_PORT = 12095;
|
|
7
|
+
/**
|
|
8
|
+
* Get the URL of an asset hosted by the COGS server.
|
|
9
|
+
*/
|
|
10
|
+
function assetUrl(file) {
|
|
11
|
+
const location = typeof window !== 'undefined' ? window.location : undefined;
|
|
12
|
+
const path = `/assets/${encodeURIComponent(file)}`;
|
|
13
|
+
return `${location === null || location === void 0 ? void 0 : location.protocol}//${location === null || location === void 0 ? void 0 : location.hostname}:${exports.COGS_SERVER_PORT}${path}`;
|
|
14
|
+
}
|
|
15
|
+
async function preloadUrl(url) {
|
|
16
|
+
const response = await fetch(url);
|
|
17
|
+
// We used arrayBuffer()` instead of `blob()` because the latter seems to fail on Pis when preloading some files
|
|
18
|
+
return URL.createObjectURL(new Blob([await response.arrayBuffer()]));
|
|
19
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type ActiveAudioClipState = {
|
|
2
|
+
type: 'paused';
|
|
3
|
+
} | {
|
|
4
|
+
type: 'pause_requested';
|
|
5
|
+
fade: number | undefined;
|
|
6
|
+
} | {
|
|
7
|
+
type: 'pausing';
|
|
8
|
+
} | {
|
|
9
|
+
type: 'playing';
|
|
10
|
+
} | {
|
|
11
|
+
type: 'play_requested';
|
|
12
|
+
} | {
|
|
13
|
+
type: 'stopping';
|
|
14
|
+
} | {
|
|
15
|
+
type: 'stop_requested';
|
|
16
|
+
fade: number | undefined;
|
|
17
|
+
};
|
|
18
|
+
export interface AudioClip {
|
|
19
|
+
config: {
|
|
20
|
+
preload: boolean;
|
|
21
|
+
ephemeral: boolean;
|
|
22
|
+
};
|
|
23
|
+
activeClips: {
|
|
24
|
+
[soundId: number]: ActiveClip;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export interface ActiveClip {
|
|
28
|
+
state: ActiveAudioClipState;
|
|
29
|
+
loop: boolean;
|
|
30
|
+
volume: number;
|
|
31
|
+
playId: string;
|
|
32
|
+
}
|
|
33
|
+
export interface AudioState {
|
|
34
|
+
isPlaying: boolean;
|
|
35
|
+
globalVolume: number;
|
|
36
|
+
clips: {
|
|
37
|
+
[path: string]: AudioClip;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { MediaObjectFit } from '..';
|
|
2
|
+
export declare enum ActiveVideoClipState {
|
|
3
|
+
Paused = "paused",
|
|
4
|
+
Playing = "playing"
|
|
5
|
+
}
|
|
6
|
+
export interface VideoClip {
|
|
7
|
+
config: {
|
|
8
|
+
preload: 'auto' | 'metadata' | 'none';
|
|
9
|
+
ephemeral: boolean;
|
|
10
|
+
fit: MediaObjectFit;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export interface ActiveClip {
|
|
14
|
+
path: string;
|
|
15
|
+
state: ActiveVideoClipState;
|
|
16
|
+
loop: boolean;
|
|
17
|
+
volume: number;
|
|
18
|
+
}
|
|
19
|
+
export interface VideoState {
|
|
20
|
+
isPlaying: boolean;
|
|
21
|
+
globalVolume: number;
|
|
22
|
+
clips: {
|
|
23
|
+
[path: string]: VideoClip;
|
|
24
|
+
};
|
|
25
|
+
activeClip?: ActiveClip;
|
|
26
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ActiveVideoClipState = void 0;
|
|
4
|
+
var ActiveVideoClipState;
|
|
5
|
+
(function (ActiveVideoClipState) {
|
|
6
|
+
ActiveVideoClipState["Paused"] = "paused";
|
|
7
|
+
ActiveVideoClipState["Playing"] = "playing";
|
|
8
|
+
})(ActiveVideoClipState || (exports.ActiveVideoClipState = ActiveVideoClipState = {}));
|
|
@@ -11,5 +11,6 @@ export declare class NetworkHostPattern {
|
|
|
11
11
|
constructor(pattern: string);
|
|
12
12
|
/** Checks whether `host` and `port` are matched by this pattern. */
|
|
13
13
|
validate(host: string, port: number): boolean;
|
|
14
|
-
|
|
14
|
+
/** Checks whether `host` is matched by the hostname part of this pattern, ignoring the port. */
|
|
15
|
+
validateHostname(host: string): boolean;
|
|
15
16
|
}
|
|
@@ -23,9 +23,10 @@ export class NetworkHostPattern {
|
|
|
23
23
|
}
|
|
24
24
|
/** Checks whether `host` and `port` are matched by this pattern. */
|
|
25
25
|
validate(host, port) {
|
|
26
|
-
return this.
|
|
26
|
+
return this.validateHostname(host) && (this.port === '*' || Number(this.port) === port);
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
/** Checks whether `host` is matched by the hostname part of this pattern, ignoring the port. */
|
|
29
|
+
validateHostname(host) {
|
|
29
30
|
// Hostnames are case-insensitive; IP literals are unaffected by lowercasing.
|
|
30
31
|
const patternHost = this.host.toLowerCase();
|
|
31
32
|
host = host.toLowerCase();
|
|
@@ -130,7 +130,7 @@ function createManifestSchema(objectSchemaFactory) {
|
|
|
130
130
|
access: z
|
|
131
131
|
.array(objectSchemaFactory({
|
|
132
132
|
hosts: z.array(z.string().refine(validateNetworkHostPattern, { error: 'Invalid network host pattern' })).min(1),
|
|
133
|
-
|
|
133
|
+
caCertificates: z.array(z.string().min(1)).optional(),
|
|
134
134
|
}))
|
|
135
135
|
.optional(),
|
|
136
136
|
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Adapted from https://stackoverflow.com/a/58325977/244640
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.setDate = setDate;
|
|
5
|
+
const OriginalDateConstructor = globalThis.Date;
|
|
6
|
+
/**
|
|
7
|
+
* Patch `Date.now()` and `new Date()` given the current time is @param now
|
|
8
|
+
*/
|
|
9
|
+
function setDate(now) {
|
|
10
|
+
const nowDelta = now - OriginalDateConstructor.now();
|
|
11
|
+
function Date(...args) {
|
|
12
|
+
if (args.length === 0) {
|
|
13
|
+
return new OriginalDateConstructor(Date.now()); // Date.now() is implemented below
|
|
14
|
+
}
|
|
15
|
+
// Specific date constructor
|
|
16
|
+
return new OriginalDateConstructor(...args);
|
|
17
|
+
}
|
|
18
|
+
// copy all properties from the original date, this includes the prototype
|
|
19
|
+
const propertyDescriptors = Object.getOwnPropertyDescriptors(OriginalDateConstructor);
|
|
20
|
+
Object.defineProperties(Date, propertyDescriptors);
|
|
21
|
+
// override Date.now to return the adjusted time
|
|
22
|
+
Date.now = function () {
|
|
23
|
+
return OriginalDateConstructor.now() + nowDelta;
|
|
24
|
+
};
|
|
25
|
+
globalThis.Date = Date;
|
|
26
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface TimeSyncRequestData {
|
|
2
|
+
timesync: {
|
|
3
|
+
id: number;
|
|
4
|
+
};
|
|
5
|
+
}
|
|
6
|
+
export interface TimeSyncResponseData {
|
|
7
|
+
timesync: {
|
|
8
|
+
id: number;
|
|
9
|
+
now: number;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export interface TimeSyncClient {
|
|
13
|
+
receive(data: TimeSyncResponseData): void;
|
|
14
|
+
destroy(): void;
|
|
15
|
+
}
|
|
16
|
+
export interface TimeSyncServer {
|
|
17
|
+
receive(data: TimeSyncRequestData): void;
|
|
18
|
+
}
|
|
19
|
+
export declare function createTimeSyncClient({ interval, send, onChange, syncSampleSize, syncRequestTimeout, }: {
|
|
20
|
+
interval: number;
|
|
21
|
+
send: (data: TimeSyncRequestData) => void | Promise<void>;
|
|
22
|
+
onChange?: (now: number) => void;
|
|
23
|
+
syncSampleSize?: number;
|
|
24
|
+
syncRequestTimeout?: number;
|
|
25
|
+
}): TimeSyncClient;
|
|
26
|
+
export declare function createTimeSyncServer({ send }: {
|
|
27
|
+
send: (data: TimeSyncResponseData) => void | Promise<void>;
|
|
28
|
+
}): TimeSyncServer;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createTimeSyncClient = createTimeSyncClient;
|
|
4
|
+
exports.createTimeSyncServer = createTimeSyncServer;
|
|
5
|
+
const setDate_1 = require("./setDate");
|
|
6
|
+
let id = 0;
|
|
7
|
+
function getId() {
|
|
8
|
+
return ++id;
|
|
9
|
+
}
|
|
10
|
+
const SYNC_SAMPLE_SIZE = 5;
|
|
11
|
+
const SYNC_REQUEST_TIMEOUT = 10000;
|
|
12
|
+
function createTimeSyncClient({ interval, send, onChange = setDate_1.setDate, syncSampleSize = SYNC_SAMPLE_SIZE, syncRequestTimeout = SYNC_REQUEST_TIMEOUT, }) {
|
|
13
|
+
const requests = {};
|
|
14
|
+
async function synchronize() {
|
|
15
|
+
const promises = [];
|
|
16
|
+
// Send a series of requests
|
|
17
|
+
for (let i = 0; i < syncSampleSize; i++) {
|
|
18
|
+
const promise = new Promise((resolve) => {
|
|
19
|
+
const id = getId();
|
|
20
|
+
const sentAt = performance.now();
|
|
21
|
+
send({ timesync: { id } });
|
|
22
|
+
const complete = (receivedAt, serverNow) => resolve({ sentAt, receivedAt, serverNow, clientNow: Date.now() });
|
|
23
|
+
requests[id] = { complete };
|
|
24
|
+
setTimeout(() => resolve(null), syncRequestTimeout);
|
|
25
|
+
});
|
|
26
|
+
promises.push(promise);
|
|
27
|
+
await promise;
|
|
28
|
+
}
|
|
29
|
+
// Perform calculation with results
|
|
30
|
+
const results = await Promise.all(promises);
|
|
31
|
+
const deltas = results
|
|
32
|
+
.filter((result) => result !== null)
|
|
33
|
+
.map((result) => {
|
|
34
|
+
const { sentAt, receivedAt, serverNow, clientNow } = result;
|
|
35
|
+
const halfLatency = (receivedAt - sentAt) / 2;
|
|
36
|
+
return clientNow - serverNow + halfLatency;
|
|
37
|
+
});
|
|
38
|
+
const averageDelta = deltas.reduce((d1, d2) => d1 + d2, 0) / deltas.length;
|
|
39
|
+
if (!isNaN(averageDelta)) {
|
|
40
|
+
onChange(Date.now() + averageDelta);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const receive = (data) => {
|
|
44
|
+
const receivedAt = performance.now();
|
|
45
|
+
const req = requests[data.timesync.id];
|
|
46
|
+
if (!req)
|
|
47
|
+
return;
|
|
48
|
+
req.complete(receivedAt, data.timesync.now);
|
|
49
|
+
};
|
|
50
|
+
synchronize();
|
|
51
|
+
const loop = setInterval(synchronize, interval);
|
|
52
|
+
const destroy = () => {
|
|
53
|
+
clearInterval(loop);
|
|
54
|
+
};
|
|
55
|
+
return {
|
|
56
|
+
receive,
|
|
57
|
+
destroy,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function createTimeSyncServer({ send }) {
|
|
61
|
+
return {
|
|
62
|
+
receive(data) {
|
|
63
|
+
const { id } = data.timesync;
|
|
64
|
+
send({ timesync: { id, now: Date.now() } });
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Connect to COGS to build a custom Media Master",
|
|
4
4
|
"author": "Clockwork Dog <info@clockwork.dog>",
|
|
5
5
|
"homepage": "https://github.com/clockwork-dog/cogs-sdk/tree/main/packages/javascript",
|
|
6
|
-
"version": "3.
|
|
6
|
+
"version": "3.2.0",
|
|
7
7
|
"keywords": [],
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"repository": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"cy:generate": "cypress run --e2e"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@clockworkdog/timesync": "^3.
|
|
40
|
+
"@clockworkdog/timesync": "^3.2.0",
|
|
41
41
|
"ip-address": "^10.2.0",
|
|
42
42
|
"reconnecting-websocket": "^4.4.0",
|
|
43
43
|
"semver": "^7.8.5",
|