mirror of
https://github.com/imgfloat/server.git
synced 2026-02-05 03:39:26 +00:00
Update source
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
const COMMAND = "!chess";
|
const COMMAND = "!chess";
|
||||||
const FEED_URL = "https://lichess.org/api/tv/feed";
|
const FEED_URL = "https://lichess.org/api/tv/feed";
|
||||||
const HEADER_HEIGHT = 40;
|
const HEADER_HEIGHT = 54;
|
||||||
const PADDING = 16;
|
const PADDING = 16;
|
||||||
const BOARD_MARGIN = 12;
|
const BOARD_MARGIN = 12;
|
||||||
const BORDER_RADIUS = 14;
|
const BORDER_RADIUS = 14;
|
||||||
const FOOTER_HEIGHT = 30;
|
const FOOTER_HEIGHT = 36;
|
||||||
const LIGHT_SQUARE = "#f0d9b5";
|
const LIGHT_SQUARE = "#f0d9b5";
|
||||||
const DARK_SQUARE = "#b58863";
|
const DARK_SQUARE = "#b58863";
|
||||||
const BOARD_OUTLINE = "#111827";
|
const BOARD_OUTLINE = "#111827";
|
||||||
@@ -217,6 +217,13 @@ function resolveClockValue(payload, color) {
|
|||||||
if (Number.isFinite(fromList?.seconds)) {
|
if (Number.isFinite(fromList?.seconds)) {
|
||||||
return fromList.seconds;
|
return fromList.seconds;
|
||||||
}
|
}
|
||||||
|
const shorthandKey = color === "white" ? "wc" : "bc";
|
||||||
|
if (Number.isFinite(payload?.[shorthandKey])) {
|
||||||
|
return payload[shorthandKey];
|
||||||
|
}
|
||||||
|
if (Number.isFinite(payload?.game?.[shorthandKey])) {
|
||||||
|
return payload.game[shorthandKey];
|
||||||
|
}
|
||||||
const direct = payload?.clocks?.[color];
|
const direct = payload?.clocks?.[color];
|
||||||
if (Number.isFinite(direct)) {
|
if (Number.isFinite(direct)) {
|
||||||
return direct;
|
return direct;
|
||||||
@@ -232,7 +239,12 @@ function formatClock(value) {
|
|||||||
if (!Number.isFinite(value)) {
|
if (!Number.isFinite(value)) {
|
||||||
return "--:--";
|
return "--:--";
|
||||||
}
|
}
|
||||||
const seconds = value > 1000 ? Math.floor(value / 100) : Math.floor(value);
|
let seconds = Math.floor(value);
|
||||||
|
if (value > 100000) {
|
||||||
|
seconds = Math.floor(value / 1000);
|
||||||
|
} else if (value > 1000) {
|
||||||
|
seconds = Math.floor(value / 100);
|
||||||
|
}
|
||||||
const minutes = Math.floor(seconds / 60);
|
const minutes = Math.floor(seconds / 60);
|
||||||
const remaining = Math.max(seconds % 60, 0);
|
const remaining = Math.max(seconds % 60, 0);
|
||||||
return `${minutes}:${String(remaining).padStart(2, "0")}`;
|
return `${minutes}:${String(remaining).padStart(2, "0")}`;
|
||||||
@@ -313,12 +325,11 @@ async function streamFeed(state) {
|
|||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
state.abortController = controller;
|
state.abortController = controller;
|
||||||
|
|
||||||
|
let streamEnded = false;
|
||||||
try {
|
try {
|
||||||
const response = await fetch(FEED_URL, {
|
const response = await fetch(FEED_URL, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: { Accept: "application/x-ndjson" },
|
||||||
Accept: "application/x-ndjson",
|
|
||||||
},
|
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
});
|
});
|
||||||
if (!response.ok || !response.body) {
|
if (!response.ok || !response.body) {
|
||||||
@@ -335,6 +346,7 @@ async function streamFeed(state) {
|
|||||||
}
|
}
|
||||||
const { value, done } = await reader.read();
|
const { value, done } = await reader.read();
|
||||||
if (done) {
|
if (done) {
|
||||||
|
streamEnded = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
buffer += decoder.decode(value, { stream: true });
|
buffer += decoder.decode(value, { stream: true });
|
||||||
@@ -365,6 +377,9 @@ async function streamFeed(state) {
|
|||||||
state.abortController = null;
|
state.abortController = null;
|
||||||
}
|
}
|
||||||
state.feedActive = false;
|
state.feedActive = false;
|
||||||
|
if (streamEnded && !state.shouldStop) {
|
||||||
|
state.shouldStop = true;
|
||||||
|
}
|
||||||
if (state.shouldStop) {
|
if (state.shouldStop) {
|
||||||
state.boardVisible = false;
|
state.boardVisible = false;
|
||||||
state.board = null;
|
state.board = null;
|
||||||
@@ -396,20 +411,27 @@ function processChatCommands(chatMessages, state) {
|
|||||||
const lastSeen = state.lastChatTimestamp ?? 0;
|
const lastSeen = state.lastChatTimestamp ?? 0;
|
||||||
let latestSeen = lastSeen;
|
let latestSeen = lastSeen;
|
||||||
let shouldStart = false;
|
let shouldStart = false;
|
||||||
|
let fallbackTimestamp = lastSeen;
|
||||||
|
|
||||||
chatMessages.forEach((message) => {
|
chatMessages.forEach((message) => {
|
||||||
const timestamp = message?.timestamp ?? 0;
|
let timestamp = message?.timestamp;
|
||||||
|
if (!Number.isFinite(timestamp)) {
|
||||||
|
fallbackTimestamp += 1;
|
||||||
|
timestamp = fallbackTimestamp;
|
||||||
|
}
|
||||||
if (timestamp <= lastSeen) {
|
if (timestamp <= lastSeen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
latestSeen = Math.max(latestSeen, timestamp);
|
latestSeen = Math.max(latestSeen, timestamp);
|
||||||
if (!shouldStart && isCommandMatch(message?.message)) {
|
if (!shouldStart && isCommandMatch(message?.message)) {
|
||||||
|
console.log("Lichess TV: Detected command to start stream feed.");
|
||||||
shouldStart = true;
|
shouldStart = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
state.lastChatTimestamp = latestSeen;
|
state.lastChatTimestamp = latestSeen;
|
||||||
|
|
||||||
|
console.log(`Lichess TV: shouldStart=${shouldStart}, feedActive=${state.feedActive}, boardVisible=${state.boardVisible}`);
|
||||||
if (shouldStart && !state.feedActive && !state.boardVisible) {
|
if (shouldStart && !state.feedActive && !state.boardVisible) {
|
||||||
streamFeed(state);
|
streamFeed(state);
|
||||||
}
|
}
|
||||||
@@ -448,20 +470,16 @@ function drawBoard(context, state) {
|
|||||||
ctx.textAlign = "left";
|
ctx.textAlign = "left";
|
||||||
ctx.textBaseline = "top";
|
ctx.textBaseline = "top";
|
||||||
|
|
||||||
const whiteLabel = state.whiteRating ? `${state.whiteName} (${state.whiteRating})` : state.whiteName || "White";
|
const metaLabel = `White (${state.whiteRating || "?"}) [${formatClock(state.whiteClock)}] versus Black (${state.blackRating || "?"}) [${formatClock(state.blackClock)}]`;
|
||||||
const blackLabel = state.blackRating ? `${state.blackName} (${state.blackRating})` : state.blackName || "Black";
|
|
||||||
const whiteClock = formatClock(state.whiteClock);
|
|
||||||
const blackClock = formatClock(state.blackClock);
|
|
||||||
|
|
||||||
ctx.fillText(blackLabel, headerX, headerY);
|
ctx.fillText(metaLabel, headerX, headerY);
|
||||||
ctx.textAlign = "right";
|
|
||||||
ctx.fillText(blackClock, panelX + panelWidth - BOARD_MARGIN, headerY);
|
|
||||||
ctx.textAlign = "left";
|
|
||||||
|
|
||||||
const statusText = state.status ? String(state.status).toUpperCase() : "LIVE";
|
const statusValue = state.status ? String(state.status).toUpperCase() : "";
|
||||||
ctx.fillStyle = TEXT_MUTED;
|
if (statusValue && statusValue !== "CONNECTING") {
|
||||||
ctx.font = "600 13px 'Inter', 'Segoe UI', sans-serif";
|
ctx.fillStyle = TEXT_MUTED;
|
||||||
ctx.fillText(statusText, headerX, headerY + 22);
|
ctx.font = "600 13px 'Inter', 'Segoe UI', sans-serif";
|
||||||
|
ctx.fillText(statusValue, headerX, headerY + 26);
|
||||||
|
}
|
||||||
|
|
||||||
const boardX = panelX + BOARD_MARGIN;
|
const boardX = panelX + BOARD_MARGIN;
|
||||||
const boardY = panelY + HEADER_HEIGHT;
|
const boardY = panelY + HEADER_HEIGHT;
|
||||||
@@ -513,15 +531,10 @@ function drawBoard(context, state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const turnIndicator = state.turn === "b" ? "Black to move" : "White to move";
|
const turnIndicator = state.turn === "b" ? "Black to move" : "White to move";
|
||||||
|
ctx.fillStyle = TEXT_PRIMARY;
|
||||||
|
ctx.textAlign = "left";
|
||||||
ctx.fillStyle = TEXT_MUTED;
|
ctx.fillStyle = TEXT_MUTED;
|
||||||
ctx.font = "500 13px 'Inter', 'Segoe UI', sans-serif";
|
ctx.textBaseline = "top";
|
||||||
ctx.textAlign = "left";
|
|
||||||
ctx.textBaseline = "bottom";
|
|
||||||
ctx.fillText(whiteLabel, panelX + BOARD_MARGIN, panelY + panelHeight - BOARD_MARGIN / 2);
|
|
||||||
ctx.textAlign = "right";
|
|
||||||
ctx.fillText(whiteClock, panelX + panelWidth - BOARD_MARGIN, panelY + panelHeight - BOARD_MARGIN / 2);
|
|
||||||
ctx.textAlign = "left";
|
|
||||||
ctx.textBaseline = "alphabetic";
|
|
||||||
ctx.font = "500 12px 'Inter', 'Segoe UI', sans-serif";
|
ctx.font = "500 12px 'Inter', 'Segoe UI', sans-serif";
|
||||||
ctx.fillText(turnIndicator, panelX + BOARD_MARGIN, panelY + panelHeight - FOOTER_HEIGHT + 12);
|
ctx.fillText(turnIndicator, panelX + BOARD_MARGIN, panelY + panelHeight - FOOTER_HEIGHT + 12);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user