feat: implement play/pause/seek sync
This commit is contained in:
commit
36d9211dce
8 changed files with 625 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
node_modules
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2024 sup39
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
2
README.md
Normal file
2
README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
# sup-ytsync
|
||||
A tampermonkey script to sync Youtube video progress with others via MQTT
|
218
index.js
Normal file
218
index.js
Normal file
|
@ -0,0 +1,218 @@
|
|||
// ==UserScript==
|
||||
// @name sup-ytsync
|
||||
// @namespace http://tampermonkey.net/
|
||||
// @version 0.1.0
|
||||
// @description sup39
|
||||
// @author sup39
|
||||
// @match https://www.youtube.com/watch*
|
||||
// @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
|
||||
// @grant none
|
||||
// ==/UserScript==
|
||||
|
||||
(() => {
|
||||
'use strict';
|
||||
|
||||
const url = '...';
|
||||
const username = '...';
|
||||
const password = '...';
|
||||
const roomId = '...';
|
||||
const clientId = `ytsync-${username}-${+new Date()}`;
|
||||
// topic = ${topicPrefixBase}/${roomId}/${videoId}/${commander.clientId}/${command}
|
||||
const topicPrefixBase = 'sup-ytsync';
|
||||
|
||||
const mqttScriptUrl = 'https://unpkg.com/mqtt@5.3.4/dist/mqtt.min.js';
|
||||
|
||||
/**
|
||||
* @param {HTMLVideoElement} element
|
||||
* @param {VideoCommandBroadcaster} commandBroadcaster
|
||||
*/
|
||||
function Video(element, commandBroadcaster) {
|
||||
/** @type {Set<string>} */
|
||||
const queuedCommands = new Set();
|
||||
|
||||
for (const [eventName, command] of (/**@type{const}*/([
|
||||
['play', 'play'],
|
||||
['pause', 'pause'],
|
||||
['seeked', 'seek'],
|
||||
]))) {
|
||||
element.addEventListener(eventName, () => {
|
||||
// ignore queued command
|
||||
if (queuedCommands.delete(command)) return;
|
||||
// ignore seeked event when playing
|
||||
if (eventName === 'seeked' && !element.paused) return;
|
||||
// callback
|
||||
const {currentTime} = element;
|
||||
commandBroadcaster(command, {currentTime});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
/** @param {number} t */
|
||||
seek(t) {
|
||||
queuedCommands.add('seek');
|
||||
element.currentTime = t;
|
||||
},
|
||||
play() {
|
||||
queuedCommands.add('play');
|
||||
element.play();
|
||||
},
|
||||
pause() {
|
||||
queuedCommands.add('pause');
|
||||
element.pause();
|
||||
},
|
||||
get isPaused() {
|
||||
return element.paused;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// FIXME
|
||||
const Buffer = /** @type{Buffer}*//**@type{any}*/(Uint8Array);
|
||||
|
||||
const videoId = new URLSearchParams(window.location.search).get('v');
|
||||
if (videoId == null) return;
|
||||
|
||||
const videoElement = document.querySelector('video');
|
||||
if (videoElement == null) return;
|
||||
|
||||
/** @returns {Promise<void>} */
|
||||
const loadMqttScript = () => new Promise(resolve => {
|
||||
if (typeof mqtt === 'object') return resolve();
|
||||
document.head.appendChild(
|
||||
Object.assign(document.createElement('script'), {
|
||||
src: mqttScriptUrl,
|
||||
async: true,
|
||||
}),
|
||||
).addEventListener('load', () => resolve());
|
||||
});
|
||||
|
||||
const startYtsync = () => loadMqttScript().then(() => {
|
||||
const topicPrefix = `${topicPrefixBase}/${roomId}/${videoId}/`;
|
||||
|
||||
console.info('Connecting to MQTT server:', url);
|
||||
const client = mqtt.connect(url, {username, password, clientId});
|
||||
client.on('error', e => console.warn('Error occurs on MQTT connection:', e));
|
||||
|
||||
/** @param {DataView} body */
|
||||
function parseTime(body) {
|
||||
if (body.byteLength !== 8) {
|
||||
throw new Error('Invalid payload');
|
||||
}
|
||||
return body.getFloat64(0);
|
||||
}
|
||||
|
||||
client.on('connect', () => {
|
||||
console.info('Connected to MQTT server:', url);
|
||||
|
||||
const video = Video(videoElement, (command, state) => {
|
||||
const {currentTime: t} = state;
|
||||
console.debug(`Broadcast video sync command [${command}]`, t);
|
||||
const payload = new Buffer(8);
|
||||
new DataView(payload.buffer).setFloat64(0, t);
|
||||
client.publish(`${topicPrefix}${clientId}/${command}`, payload);
|
||||
});
|
||||
|
||||
client.on('message', (/**@type{string}*/topic, /**@type{Uint8Array}*/payload) => {
|
||||
if (!topic.startsWith(topicPrefix)) {
|
||||
return console.warn('Unexpected MQTT message', {topic, payload});
|
||||
}
|
||||
const [commander, command=''] = topic.slice(topicPrefix.length).split('/');
|
||||
// ignore command sent by self
|
||||
if (commander === clientId) return;
|
||||
|
||||
/** DataView of payload */
|
||||
const body = new DataView(
|
||||
payload.buffer,
|
||||
payload.byteOffset,
|
||||
payload.byteLength,
|
||||
);
|
||||
|
||||
try {
|
||||
if (command === 'play') {
|
||||
const t = parseTime(body);
|
||||
console.debug(`Received command [${command}]`, t);
|
||||
video.seek(t);
|
||||
video.play();
|
||||
} else if (command === 'pause') {
|
||||
const t = parseTime(body);
|
||||
console.debug(`Received command [${command}]`, t);
|
||||
video.seek(t);
|
||||
video.pause();
|
||||
} else if (command === 'seek') {
|
||||
/**
|
||||
* (seek while playing) pause -> play -> seeked
|
||||
* => no need to update current time
|
||||
* (seek when paused) pause -> seeked
|
||||
* => need to update current time
|
||||
*/
|
||||
if (video.isPaused) {
|
||||
const t = parseTime(body);
|
||||
console.debug(`Received command [${command}]`, t);
|
||||
video.seek(t);
|
||||
}
|
||||
} else {
|
||||
return console.warn('Unknown command', {command, payload});
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e, {topic, payload});
|
||||
}
|
||||
});
|
||||
|
||||
client.subscribe(topicPrefix+'+/+');
|
||||
});
|
||||
});
|
||||
|
||||
// Based on https://stackoverflow.com/a/61511955 by Yong Wang (2020)
|
||||
/**
|
||||
* @param {string} selector
|
||||
* @returns {Promise<Element>}
|
||||
*/
|
||||
const untilElementLoaded = selector => new Promise(resolve => {
|
||||
const elm = document.querySelector(selector);
|
||||
if (elm != null) return elm;
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
const elm = document.querySelector(selector);
|
||||
if (elm != null) {
|
||||
observer.disconnect();
|
||||
resolve(elm);
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
});
|
||||
|
||||
// TODO better UI
|
||||
untilElementLoaded('#title h1').then(elm => elm.appendChild(
|
||||
Object.assign(document.createElement('button'), {
|
||||
textContent: 'Start sup-ytsync'
|
||||
}),
|
||||
).addEventListener('click', startYtsync, {once: true}));
|
||||
})();
|
||||
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2024 sup39
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
6
jsconfig.json
Normal file
6
jsconfig.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"checkJs": true,
|
||||
"strict": true
|
||||
}
|
||||
}
|
11
package.json
Normal file
11
package.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "sup-ytsync",
|
||||
"version": "0.1.0",
|
||||
"description": "A tampermonkey script to sync Youtube video progress with others via MQTT",
|
||||
"main": "index.js",
|
||||
"author": "sup39",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"mqtt": "^5.3.4"
|
||||
}
|
||||
}
|
353
pnpm-lock.yaml
Normal file
353
pnpm-lock.yaml
Normal file
|
@ -0,0 +1,353 @@
|
|||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
devDependencies:
|
||||
mqtt:
|
||||
specifier: ^5.3.4
|
||||
version: 5.3.4
|
||||
|
||||
packages:
|
||||
|
||||
/@babel/runtime@7.23.8:
|
||||
resolution: {integrity: sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
regenerator-runtime: 0.14.1
|
||||
dev: true
|
||||
|
||||
/@types/node@20.11.5:
|
||||
resolution: {integrity: sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==}
|
||||
dependencies:
|
||||
undici-types: 5.26.5
|
||||
dev: true
|
||||
|
||||
/@types/readable-stream@4.0.10:
|
||||
resolution: {integrity: sha512-AbUKBjcC8SHmImNi4yK2bbjogQlkFSg7shZCcicxPQapniOlajG8GCc39lvXzCWX4lLRRs7DM3VAeSlqmEVZUA==}
|
||||
dependencies:
|
||||
'@types/node': 20.11.5
|
||||
safe-buffer: 5.1.2
|
||||
dev: true
|
||||
|
||||
/@types/ws@8.5.10:
|
||||
resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
|
||||
dependencies:
|
||||
'@types/node': 20.11.5
|
||||
dev: true
|
||||
|
||||
/abort-controller@3.0.0:
|
||||
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
||||
engines: {node: '>=6.5'}
|
||||
dependencies:
|
||||
event-target-shim: 5.0.1
|
||||
dev: true
|
||||
|
||||
/balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
dev: true
|
||||
|
||||
/base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
dev: true
|
||||
|
||||
/bl@6.0.10:
|
||||
resolution: {integrity: sha512-F14DFhDZfxtVm2FY0k9kG2lWAwzZkO9+jX3Ytuoy/V0E1/5LBuBzzQHXAjqpxXEDIpmTPZZf5GVIGPQcLxFpaA==}
|
||||
dependencies:
|
||||
buffer: 6.0.3
|
||||
inherits: 2.0.4
|
||||
readable-stream: 4.5.2
|
||||
dev: true
|
||||
|
||||
/brace-expansion@2.0.1:
|
||||
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
dev: true
|
||||
|
||||
/buffer-from@1.1.2:
|
||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||
dev: true
|
||||
|
||||
/buffer@6.0.3:
|
||||
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
ieee754: 1.2.1
|
||||
dev: true
|
||||
|
||||
/commist@3.2.0:
|
||||
resolution: {integrity: sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==}
|
||||
dev: true
|
||||
|
||||
/concat-stream@2.0.0:
|
||||
resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==}
|
||||
engines: {'0': node >= 6.0}
|
||||
dependencies:
|
||||
buffer-from: 1.1.2
|
||||
inherits: 2.0.4
|
||||
readable-stream: 3.6.2
|
||||
typedarray: 0.0.6
|
||||
dev: true
|
||||
|
||||
/debug@4.3.4:
|
||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
dependencies:
|
||||
ms: 2.1.2
|
||||
dev: true
|
||||
|
||||
/event-target-shim@5.0.1:
|
||||
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
|
||||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/events@3.3.0:
|
||||
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
|
||||
engines: {node: '>=0.8.x'}
|
||||
dev: true
|
||||
|
||||
/fast-unique-numbers@8.0.13:
|
||||
resolution: {integrity: sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==}
|
||||
engines: {node: '>=16.1.0'}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.23.8
|
||||
tslib: 2.6.2
|
||||
dev: true
|
||||
|
||||
/fs.realpath@1.0.0:
|
||||
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||
dev: true
|
||||
|
||||
/glob@8.1.0:
|
||||
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
fs.realpath: 1.0.0
|
||||
inflight: 1.0.6
|
||||
inherits: 2.0.4
|
||||
minimatch: 5.1.6
|
||||
once: 1.4.0
|
||||
dev: true
|
||||
|
||||
/help-me@4.2.0:
|
||||
resolution: {integrity: sha512-TAOnTB8Tz5Dw8penUuzHVrKNKlCIbwwbHnXraNJxPwf8LRtE2HlM84RYuezMFcwOJmoYOCWVDyJ8TQGxn9PgxA==}
|
||||
dependencies:
|
||||
glob: 8.1.0
|
||||
readable-stream: 3.6.2
|
||||
dev: true
|
||||
|
||||
/ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
dev: true
|
||||
|
||||
/inflight@1.0.6:
|
||||
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
|
||||
dependencies:
|
||||
once: 1.4.0
|
||||
wrappy: 1.0.2
|
||||
dev: true
|
||||
|
||||
/inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
dev: true
|
||||
|
||||
/js-sdsl@4.3.0:
|
||||
resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==}
|
||||
dev: true
|
||||
|
||||
/lru-cache@10.1.0:
|
||||
resolution: {integrity: sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==}
|
||||
engines: {node: 14 || >=16.14}
|
||||
dev: true
|
||||
|
||||
/minimatch@5.1.6:
|
||||
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
brace-expansion: 2.0.1
|
||||
dev: true
|
||||
|
||||
/minimist@1.2.8:
|
||||
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
|
||||
dev: true
|
||||
|
||||
/mqtt-packet@9.0.0:
|
||||
resolution: {integrity: sha512-8v+HkX+fwbodsWAZIZTI074XIoxVBOmPeggQuDFCGg1SqNcC+uoRMWu7J6QlJPqIUIJXmjNYYHxBBLr1Y/Df4w==}
|
||||
dependencies:
|
||||
bl: 6.0.10
|
||||
debug: 4.3.4
|
||||
process-nextick-args: 2.0.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/mqtt@5.3.4:
|
||||
resolution: {integrity: sha512-nyhr2bnFtyiv68jV3yfR6eQtGcGs/jr2l3ETKXYc0amttsasXa1KgvETHRNRjfeDt/yc68IqoEjFzKkHpoQUPQ==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@types/readable-stream': 4.0.10
|
||||
'@types/ws': 8.5.10
|
||||
commist: 3.2.0
|
||||
concat-stream: 2.0.0
|
||||
debug: 4.3.4
|
||||
help-me: 4.2.0
|
||||
lru-cache: 10.1.0
|
||||
minimist: 1.2.8
|
||||
mqtt-packet: 9.0.0
|
||||
number-allocator: 1.0.14
|
||||
readable-stream: 4.5.2
|
||||
reinterval: 1.1.0
|
||||
rfdc: 1.3.1
|
||||
split2: 4.2.0
|
||||
worker-timers: 7.1.1
|
||||
ws: 8.16.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
dev: true
|
||||
|
||||
/ms@2.1.2:
|
||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||
dev: true
|
||||
|
||||
/number-allocator@1.0.14:
|
||||
resolution: {integrity: sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==}
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
js-sdsl: 4.3.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
dev: true
|
||||
|
||||
/process-nextick-args@2.0.1:
|
||||
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
||||
dev: true
|
||||
|
||||
/process@0.11.10:
|
||||
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
dev: true
|
||||
|
||||
/readable-stream@3.6.2:
|
||||
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
|
||||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
string_decoder: 1.3.0
|
||||
util-deprecate: 1.0.2
|
||||
dev: true
|
||||
|
||||
/readable-stream@4.5.2:
|
||||
resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dependencies:
|
||||
abort-controller: 3.0.0
|
||||
buffer: 6.0.3
|
||||
events: 3.3.0
|
||||
process: 0.11.10
|
||||
string_decoder: 1.3.0
|
||||
dev: true
|
||||
|
||||
/regenerator-runtime@0.14.1:
|
||||
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
|
||||
dev: true
|
||||
|
||||
/reinterval@1.1.0:
|
||||
resolution: {integrity: sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==}
|
||||
dev: true
|
||||
|
||||
/rfdc@1.3.1:
|
||||
resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==}
|
||||
dev: true
|
||||
|
||||
/safe-buffer@5.1.2:
|
||||
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
|
||||
dev: true
|
||||
|
||||
/safe-buffer@5.2.1:
|
||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||
dev: true
|
||||
|
||||
/split2@4.2.0:
|
||||
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
|
||||
engines: {node: '>= 10.x'}
|
||||
dev: true
|
||||
|
||||
/string_decoder@1.3.0:
|
||||
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
dev: true
|
||||
|
||||
/tslib@2.6.2:
|
||||
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
|
||||
dev: true
|
||||
|
||||
/typedarray@0.0.6:
|
||||
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
|
||||
dev: true
|
||||
|
||||
/undici-types@5.26.5:
|
||||
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
|
||||
dev: true
|
||||
|
||||
/util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
dev: true
|
||||
|
||||
/worker-timers-broker@6.1.1:
|
||||
resolution: {integrity: sha512-CTlDnkXAewtYvw5gOwVIc6UuIPcNHJrqWxBMhZbCWOmadvl20nPs9beAsXlaTEwW3G2KBpuKiSgkhBkhl3mxDA==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.23.8
|
||||
fast-unique-numbers: 8.0.13
|
||||
tslib: 2.6.2
|
||||
worker-timers-worker: 7.0.65
|
||||
dev: true
|
||||
|
||||
/worker-timers-worker@7.0.65:
|
||||
resolution: {integrity: sha512-Dl4nGONr8A8Fr+vQnH7Ee+o2iB480S1fBcyJYqnMyMwGRVyQZLZU+o91vbMvU1vHqiryRQmjXzzMYlh86wx+YQ==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.23.8
|
||||
tslib: 2.6.2
|
||||
dev: true
|
||||
|
||||
/worker-timers@7.1.1:
|
||||
resolution: {integrity: sha512-axtq83GwPqYwkQmQmei2abQ9cT7oSwmLw4lQCZ9VmMH9g4t4kuEF1Gw+tdnIJGHCiZ2QPDnr/+307bYx6tynLA==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.23.8
|
||||
tslib: 2.6.2
|
||||
worker-timers-broker: 6.1.1
|
||||
worker-timers-worker: 7.0.65
|
||||
dev: true
|
||||
|
||||
/wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
dev: true
|
||||
|
||||
/ws@8.16.0:
|
||||
resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
dev: true
|
13
types.d.ts
vendored
Normal file
13
types.d.ts
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
import MQTT from 'mqtt';
|
||||
declare global {
|
||||
const mqtt: typeof MQTT;
|
||||
|
||||
type VideoCommand = 'play' | 'pause' | 'seek';
|
||||
type VideoState = {
|
||||
currentTime: number
|
||||
};
|
||||
type VideoCommandBroadcaster = (
|
||||
command: VideoCommand,
|
||||
state: VideoState
|
||||
) => void;
|
||||
}
|
Loading…
Reference in a new issue