mirror of
https://github.com/imgfloat/server.git
synced 2026-02-05 03:39:26 +00:00
Add kirov_reporting script asset tto marketplace
This commit is contained in:
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -21,3 +21,7 @@ doc/marketplace-scripts/color-bars/logo.png filter=lfs diff=lfs merge=lfs -text
|
|||||||
doc/marketplace-scripts/mobius-strip/logo.png filter=lfs diff=lfs merge=lfs -text
|
doc/marketplace-scripts/mobius-strip/logo.png filter=lfs diff=lfs merge=lfs -text
|
||||||
doc/marketplace-scripts/rotating-logo/attachments/rotate.png filter=lfs diff=lfs merge=lfs -text
|
doc/marketplace-scripts/rotating-logo/attachments/rotate.png filter=lfs diff=lfs merge=lfs -text
|
||||||
doc/marketplace-scripts/rotating-logo/logo.png filter=lfs diff=lfs merge=lfs -text
|
doc/marketplace-scripts/rotating-logo/logo.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
doc/marketplace-scripts/chat-overlay/logo.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
doc/marketplace-scripts/kirov-reporting/logo.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
doc/marketplace-scripts/teapot-viewer/logo.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
doc/marketplace-scripts/kirov-reporting/attachments/kirov_reporting.mp3 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|||||||
BIN
doc/marketplace-scripts/chat-overlay/logo.png
LFS
Normal file
BIN
doc/marketplace-scripts/chat-overlay/logo.png
LFS
Normal file
Binary file not shown.
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"name": "Chat Overlay",
|
"name": "Chat Overlay",
|
||||||
"description": "Render the last two minutes of Twitch chat messages on the broadcast canvas.",
|
"description": "Render the last two minutes of Twitch chat messages on the broadcast canvas."
|
||||||
"broadcaster": "System"
|
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
doc/marketplace-scripts/kirov-reporting/attachments/kirov_reporting.mp3
LFS
Normal file
BIN
doc/marketplace-scripts/kirov-reporting/attachments/kirov_reporting.mp3
LFS
Normal file
Binary file not shown.
BIN
doc/marketplace-scripts/kirov-reporting/logo.png
LFS
Normal file
BIN
doc/marketplace-scripts/kirov-reporting/logo.png
LFS
Normal file
Binary file not shown.
4
doc/marketplace-scripts/kirov-reporting/metadata.json
Normal file
4
doc/marketplace-scripts/kirov-reporting/metadata.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "Kirov Reporting!",
|
||||||
|
"description": "Plays kirov_reporting.mp3 whenever !kirov is typed in chat."
|
||||||
|
}
|
||||||
73
doc/marketplace-scripts/kirov-reporting/source.js
Normal file
73
doc/marketplace-scripts/kirov-reporting/source.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
const COMMAND = "!kirov";
|
||||||
|
const COOLDOWN_MS = 5000;
|
||||||
|
const ATTACHMENT_NAME = "kirov_reporting.mp3";
|
||||||
|
|
||||||
|
function normalizeMessage(message) {
|
||||||
|
return (message || "").trim().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCommandMatch(message) {
|
||||||
|
const normalized = normalizeMessage(message);
|
||||||
|
if (!normalized.startsWith(COMMAND)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return normalized.length === COMMAND.length || normalized.startsWith(`${COMMAND} `);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveAttachmentId(assets, state) {
|
||||||
|
if (state?.attachmentId) {
|
||||||
|
return state.attachmentId;
|
||||||
|
}
|
||||||
|
if (!Array.isArray(assets)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const match = assets.find((asset) => asset?.name?.toLowerCase() === ATTACHMENT_NAME);
|
||||||
|
if (!match?.id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
state.attachmentId = match.id;
|
||||||
|
return match.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tick(context, state) {
|
||||||
|
const { chatMessages, assets, now, playAudio } = context;
|
||||||
|
if (!Array.isArray(chatMessages) || chatMessages.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof playAudio !== "function") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const lastSeen = state.lastChatTimestamp ?? 0;
|
||||||
|
let latestSeen = lastSeen;
|
||||||
|
const currentTime = Number.isFinite(now) ? now : 0;
|
||||||
|
const lastTriggerAt = state.lastTriggerAt ?? -Infinity;
|
||||||
|
let nextTriggerAt = lastTriggerAt;
|
||||||
|
let shouldTrigger = false;
|
||||||
|
|
||||||
|
chatMessages.forEach((message) => {
|
||||||
|
const timestamp = message?.timestamp ?? 0;
|
||||||
|
if (timestamp <= lastSeen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
latestSeen = Math.max(latestSeen, timestamp);
|
||||||
|
if (!shouldTrigger && isCommandMatch(message?.message)) {
|
||||||
|
if (currentTime - lastTriggerAt >= COOLDOWN_MS) {
|
||||||
|
shouldTrigger = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
state.lastChatTimestamp = latestSeen;
|
||||||
|
|
||||||
|
if (!shouldTrigger) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const attachmentId = resolveAttachmentId(assets, state);
|
||||||
|
if (!attachmentId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
playAudio(attachmentId);
|
||||||
|
state.lastTriggerAt = currentTime;
|
||||||
|
}
|
||||||
BIN
doc/marketplace-scripts/teapot-viewer/logo.png
LFS
Normal file
BIN
doc/marketplace-scripts/teapot-viewer/logo.png
LFS
Normal file
Binary file not shown.
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"name": "Teapot 3D Viewer",
|
"name": "Teapot 3D Viewer",
|
||||||
"description": "Renders the classic Utah teapot using a 3D model attachment.",
|
"description": "Renders the classic Utah teapot using a 3D model attachment."
|
||||||
"broadcaster": "System"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export class BroadcastRenderer {
|
|||||||
this.scriptWorkerReady = false;
|
this.scriptWorkerReady = false;
|
||||||
this.scriptErrorKeys = new Set();
|
this.scriptErrorKeys = new Set();
|
||||||
this.scriptAttachmentCache = new Map();
|
this.scriptAttachmentCache = new Map();
|
||||||
|
this.scriptAttachmentsByAssetId = new Map();
|
||||||
this.chatMessages = [];
|
this.chatMessages = [];
|
||||||
|
|
||||||
this.obsBrowser = !!globalThis.obsstudio;
|
this.obsBrowser = !!globalThis.obsstudio;
|
||||||
@@ -470,6 +471,10 @@ export class BroadcastRenderer {
|
|||||||
|
|
||||||
handleScriptWorkerMessage(event) {
|
handleScriptWorkerMessage(event) {
|
||||||
const { type, payload } = event.data || {};
|
const { type, payload } = event.data || {};
|
||||||
|
if (type === "scriptAudio") {
|
||||||
|
this.playScriptAudio(payload);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (type !== "scriptError" || !payload?.id) {
|
if (type !== "scriptError" || !payload?.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -519,6 +524,8 @@ export class BroadcastRenderer {
|
|||||||
console.error(`Unable to fetch asset ${asset.id} from ${asset.url}`, error);
|
console.error(`Unable to fetch asset ${asset.id} from ${asset.url}`, error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const attachments = await this.resolveScriptAttachments(asset.scriptAttachments);
|
||||||
|
this.scriptAttachmentsByAssetId.set(asset.id, attachments);
|
||||||
this.scriptWorker.postMessage({
|
this.scriptWorker.postMessage({
|
||||||
type: "addScript",
|
type: "addScript",
|
||||||
payload: {
|
payload: {
|
||||||
@@ -527,7 +534,7 @@ export class BroadcastRenderer {
|
|||||||
canvas: offscreen,
|
canvas: offscreen,
|
||||||
width: scriptCanvas.width,
|
width: scriptCanvas.width,
|
||||||
height: scriptCanvas.height,
|
height: scriptCanvas.height,
|
||||||
attachments: await this.resolveScriptAttachments(asset.scriptAttachments),
|
attachments,
|
||||||
},
|
},
|
||||||
}, [offscreen]);
|
}, [offscreen]);
|
||||||
}
|
}
|
||||||
@@ -536,11 +543,13 @@ export class BroadcastRenderer {
|
|||||||
if (!this.scriptWorker || !this.scriptWorkerReady || !asset?.id) {
|
if (!this.scriptWorker || !this.scriptWorkerReady || !asset?.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const attachments = await this.resolveScriptAttachments(asset.scriptAttachments);
|
||||||
|
this.scriptAttachmentsByAssetId.set(asset.id, attachments);
|
||||||
this.scriptWorker.postMessage({
|
this.scriptWorker.postMessage({
|
||||||
type: "updateAttachments",
|
type: "updateAttachments",
|
||||||
payload: {
|
payload: {
|
||||||
id: asset.id,
|
id: asset.id,
|
||||||
attachments: await this.resolveScriptAttachments(asset.scriptAttachments),
|
attachments,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -553,9 +562,29 @@ export class BroadcastRenderer {
|
|||||||
type: "removeScript",
|
type: "removeScript",
|
||||||
payload: { id: assetId },
|
payload: { id: assetId },
|
||||||
});
|
});
|
||||||
|
this.scriptAttachmentsByAssetId.delete(assetId);
|
||||||
this.removeScriptCanvas(assetId);
|
this.removeScriptCanvas(assetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
playScriptAudio(payload) {
|
||||||
|
if (!payload?.scriptId || !payload?.attachmentId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const attachments = this.scriptAttachmentsByAssetId.get(payload.scriptId);
|
||||||
|
if (!Array.isArray(attachments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const attachment = attachments.find((item) => item?.id === payload.attachmentId);
|
||||||
|
if (!attachment?.url || !attachment?.mediaType?.startsWith("audio/")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const audioAsset = {
|
||||||
|
id: `script-${payload.scriptId}-${attachment.id}`,
|
||||||
|
url: attachment.url,
|
||||||
|
};
|
||||||
|
this.audioManager.playAudioImmediately(audioAsset);
|
||||||
|
}
|
||||||
|
|
||||||
ensureScriptCanvas(assetId) {
|
ensureScriptCanvas(assetId) {
|
||||||
if (!assetId || !this.scriptLayer) {
|
if (!assetId || !this.scriptLayer) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ function stopTickLoopIfIdle() {
|
|||||||
|
|
||||||
function createScriptHandlers(source, context, state, sourceLabel = "") {
|
function createScriptHandlers(source, context, state, sourceLabel = "") {
|
||||||
const contextPrelude =
|
const contextPrelude =
|
||||||
"const { canvas, ctx, channelName, width, height, now, deltaMs, elapsedMs, assets, chatMessages } = context;";
|
"const { canvas, ctx, channelName, width, height, now, deltaMs, elapsedMs, assets, chatMessages, playAudio } = context;";
|
||||||
const sourceUrl = sourceLabel ? `\n//# sourceURL=${sourceLabel}` : "";
|
const sourceUrl = sourceLabel ? `\n//# sourceURL=${sourceLabel}` : "";
|
||||||
const factory = new Function(
|
const factory = new Function(
|
||||||
"context",
|
"context",
|
||||||
@@ -242,6 +242,19 @@ self.addEventListener("message", (event) => {
|
|||||||
elapsedMs: 0,
|
elapsedMs: 0,
|
||||||
assets: Array.isArray(payload.attachments) ? payload.attachments : [],
|
assets: Array.isArray(payload.attachments) ? payload.attachments : [],
|
||||||
chatMessages,
|
chatMessages,
|
||||||
|
playAudio: (attachment) => {
|
||||||
|
const attachmentId = typeof attachment === "string" ? attachment : attachment?.id;
|
||||||
|
if (!attachmentId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.postMessage({
|
||||||
|
type: "scriptAudio",
|
||||||
|
payload: {
|
||||||
|
scriptId: payload.id,
|
||||||
|
attachmentId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
let handlers = {};
|
let handlers = {};
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user