Skip to content

Commit f489aa4

Browse files
committed
Post WF content gen
1 parent df3dbf8 commit f489aa4

16 files changed

+2669
-2231
lines changed

docs/frontend-tricks.md

+7
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,10 @@ You can pass forces visual config using the forceVisualConfig
1616

1717
Examples:
1818
- `http://host/overelay?forceVisualConfig=`
19+
20+
## onlyWidgets
21+
You can pass onlyWidgets query params to show only the widgets that are passed in the query params by their id.
22+
23+
Examples:
24+
- `http://host/overlay?onlyWidgets=queue`
25+
show only queue

src/frontend/content-gen/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ tests/screenshots/
55
playwright-report/
66
blob-report/
77
playwright/.cache/
8+
videos
9+
config
+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#!/bin/bash
2+
set -e
3+
set -o pipefail
4+
# set -x
5+
6+
OFFSET=4
7+
SPEEDUP=15
8+
CONTEST_NAME="finals"
9+
REPO_ROOT=$(git rev-parse --show-toplevel)
10+
11+
# echo "Step 1: Download latest event-feed.json"
12+
# rm -f event-feed-$CONTEST_NAME.ndjson
13+
# timeout 10s wget --header 'cookie: JSESSIONID=0000ij9lihNPu7Xe7kc_R9VimX0:cde8627c-75ce-43c7-a09a-af5b62653e86:9eb3971a-d235-44b6-af0e-68c25f91e474:6e590761-52bd-44b0-8ab8-bb8884df8caf' \ -k https://172.24.0.7:7443/api/contests/wf48_$CONTEST_NAME/event-feed -q -O event-feed-$CONTEST_NAME.ndjson || true
14+
15+
# echo "Step 2: Edit the config for backend"
16+
# cp $REPO_ROOT/artifacts/live-v3-dev.jar ./
17+
# rm -rf config
18+
# rsync -arv --exclude ach --exclude advanced.json /Volumes/C\$/work/overlay/config/ config
19+
20+
# mv event-feed-$CONTEST_NAME.ndjson config/$CONTEST_NAME/event-feed.ndjson
21+
# we do a little trolling
22+
# cp config/systest2_replay/event-feed-systest2.ndjson config/$CONTEST_NAME/event-feed.ndjson
23+
24+
# jq '.feeds[0].source = "."' config/$CONTEST_NAME/settings.json > config/$CONTEST_NAME/settings.json.tmp
25+
# mv config/$CONTEST_NAME/settings.json.tmp config/$CONTEST_NAME/settings.json
26+
27+
startTime=$(python3 - <<EOF
28+
import datetime
29+
delta=datetime.timedelta(hours=$OFFSET)
30+
padding = datetime.timedelta(seconds=15)
31+
print((datetime.datetime.now(datetime.timezone.utc).astimezone() - delta / $SPEEDUP + padding).isoformat())
32+
EOF
33+
)
34+
echo "Start time: $startTime"
35+
36+
cat <<EOF > config/$CONTEST_NAME/settings.json
37+
{
38+
"type": "clics",
39+
"network": { "allowUnsecureConnections": true },
40+
"feeds": [
41+
{
42+
"source": ".",
43+
"contestId": "",
44+
"eventFeedPath": "",
45+
"eventFeedName": "event-feed.ndjson",
46+
"urlPrefixMapping": {
47+
"https://172.24.0.7:7443/": "http://172.24.0.20:4323/",
48+
"contests": "http://172.24.0.20:4323/api/contests"
49+
}
50+
}
51+
],
52+
"emulation": {
53+
"speed": $SPEEDUP,
54+
"startTime": "$startTime",
55+
}
56+
}
57+
EOF
58+
59+
echo "Step 3: add ticker messages"
60+
cat <<EOF > config/$CONTEST_NAME/presets/ticker.json
61+
[
62+
{
63+
"type": "text",
64+
"part": "long",
65+
"periodMs": 30000,
66+
"text": "REPLAY"
67+
},
68+
{
69+
"type": "text",
70+
"part": "short",
71+
"periodMs": 30000,
72+
"text": "ICPCLive"
73+
}
74+
]
75+
EOF
76+
77+
echo "Step 4: Start backend"
78+
java -jar live-v3-dev.jar -c config/$CONTEST_NAME --custom-fields-csv=config/custom-fields.csv --no-auth > ./backend.log &
79+
BACKEND_PID=$!
80+
function cleanup {
81+
echo "Step INF: Cleanup"
82+
kill $BACKEND_PID
83+
wait $BACKEND_PID || true
84+
}
85+
trap cleanup EXIT
86+
sleep 5
87+
88+
echo "Step 5: Show ticker messages"
89+
curl -X POST 'http://localhost:8080/api/admin/tickerMessage/1/show' -v
90+
curl -X POST 'http://localhost:8080/api/admin/tickerMessage/2/show' -v
91+
92+
echo "Step 6: Start video generation"
93+
stopwatch() {
94+
start=$(gdate +%s)
95+
while true; do
96+
time="$(( $(gdate +%s) - $start ))"
97+
printf '%s\r' "$(gdate -u -d "@$time" +%H:%M:%S)"
98+
sleep 0.1
99+
done
100+
}
101+
stopwatch &
102+
STOPWATCH_PID=$!
103+
./node_modules/.bin/playwright test tests/story.spec.ts
104+
kill $STOPWATCH_PID
105+
106+
107+
echo "Step 7: Reencode to mp4 60fps and speed up the result video"
108+
lastVideo=$(ls -t videos/*.webm | head -n1)
109+
echo "Last video: $lastVideo"
110+
targetLength=55
111+
speedup=$(echo "scale=4; (60 / $SPEEDUP) / ($targetLength / 60)" | bc)
112+
ffmpeg -i $lastVideo -vf "setpts=PTS/$speedup" -r 60 -c:v libx264 -crf 23 -c:a aac -b:a 128k -y $lastVideo-offset-$OFFSET-$targetLength.mp4
113+
targetLength=12
114+
speedup=$(echo "scale=4; (60 / $SPEEDUP) / ($targetLength / 60)" | bc)
115+
ffmpeg -i $lastVideo -vf "setpts=PTS/$speedup" -r 60 -c:v libx264 -crf 23 -c:a aac -b:a 128k -y $lastVideo-offset-$OFFSET-$targetLength.mp4
116+
117+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"use strict";
2+
3+
import WebSocket from "ws";
4+
import * as fs from "node:fs"
5+
import * as path from "node:path";
6+
export const BACKEND_URL = process.env.BACKEND_URL ?? "http://localhost:8080";
7+
8+
const contestInfo = await new Promise((resolve, reject) => {
9+
console.log("Opening websocket")
10+
const websocket = new WebSocket(`${BACKEND_URL.replace('http', 'ws')}/api/overlay/contestInfo`)
11+
websocket.onmessage = (message) => {
12+
resolve(JSON.parse(message.data));
13+
websocket.close()
14+
}
15+
})
16+
17+
fs.writeFileSync("contestInfo.json", JSON.stringify(contestInfo))

src/frontend/content-gen/package-lock.json

-74
This file was deleted.

src/frontend/content-gen/package.json

+6-2
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@
77
"author": "",
88
"license": "ISC",
99
"devDependencies": {
10-
"@playwright/test": "^1.32.3",
10+
"@playwright/test": "^1.47.1",
1111
"@types/node": "^22.5.4",
1212
"fs": "0.0.1-security",
13-
"path": "^0.12.7"
13+
"path": "^0.12.7",
14+
"ts-node": "^10.9.2"
1415
},
1516
"scripts": {
1617
"install-browsers": "playwright install --with-deps",
1718
"teamview": "playwright test teamview.spec.ts"
19+
},
20+
"dependencies": {
21+
"ws": "^8.13.0"
1822
}
1923
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import test, { ConsoleMessage, expect } from "@playwright/test";
2+
3+
const minutes10 = 1000 * 60 * 10;
4+
const WIDTH = 1080;
5+
const HEIGHT = 1920;
6+
const visualConfig = {
7+
SCREEN_WIDTH: WIDTH,
8+
SCREEN_HEIGHT: HEIGHT,
9+
TICKER_SMALL_SIZE: "50%",
10+
TICKER_LIVE_ICON_SIZE: "70px",
11+
SCOREBOARD_CELL_PLACE_SIZE: "50px",
12+
SCOREBOARD_CELL_TEAMNAME_SIZE: "304px",
13+
SCOREBOARD_CELL_POINTS_SIZE: "50px",
14+
SCOREBOARD_CELL_PENALTY_SIZE: "92px",
15+
FULL_SCREEN_CLOCK_FONT_SIZE: "300px",
16+
FULL_SCREEN_CLOCK_COLOR: "#eeeeee33",
17+
BACKGROUND: "#691930"
18+
};
19+
const widgets = [
20+
{
21+
type: "ScoreboardWidget",
22+
widgetId: "scoreboard",
23+
location: {
24+
positionX: 16,
25+
positionY: 148,
26+
sizeX: 1048,
27+
sizeY: 1756,
28+
},
29+
statisticsId: "scoreboard",
30+
settings: {
31+
scrollDirection: "FirstPage",
32+
optimismLevel: "normal",
33+
group: "all",
34+
},
35+
},
36+
{
37+
type: "TickerWidget",
38+
widgetId: "ticker",
39+
location: {
40+
positionX: 540,
41+
positionY: 46,
42+
sizeX: 524,
43+
sizeY: 86,
44+
},
45+
statisticsId: "ticker",
46+
settings: {},
47+
},
48+
{
49+
type: "FullScreenClockWidget",
50+
widgetId: "fullScreenClock",
51+
location: {
52+
positionX: 16,
53+
positionY: 148,
54+
sizeX: 1048,
55+
sizeY: 1756,
56+
},
57+
statisticsId: "fullScreenClock",
58+
settings: {
59+
globalTimeMode: false,
60+
quietMode: false,
61+
contestCountdownMode: false,
62+
},
63+
},
64+
];
65+
test.describe("Instagram story", async () => {
66+
test.setTimeout(minutes10);
67+
test("Instagram story", async ({ browser }) => {
68+
const context = await browser.newContext({
69+
recordVideo: { dir: "videos/", size: { width: WIDTH, height: HEIGHT } },
70+
viewport: { width: WIDTH, height: HEIGHT },
71+
});
72+
const page = await context.newPage();
73+
// const messages: ConsoleMessage[] = [];
74+
// page.on('console', m => messages.push(m));
75+
const url = `localhost:8080/overlay?forceWidgets=${encodeURIComponent(
76+
JSON.stringify(widgets)
77+
)}&forceVisualConfig=${encodeURIComponent(JSON.stringify(visualConfig))}`;
78+
console.log(url);
79+
await page.goto(url);
80+
const selector = await page.waitForSelector(
81+
'[data-widget-id="scoreboard"]'
82+
);
83+
const locator = await page.locator('[data-widget-id="scoreboard"]');
84+
await page.waitForTimeout(4 * 60 * 1000 + 15 * 1000);
85+
// await expect(locator).toHaveText('OVER', { timeout: 100000000})
86+
await context.close();
87+
// expect(messages).toEqual([]);
88+
});
89+
});

src/frontend/content-gen/tests/teamview.spec.ts

+1-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import test, { PlaywrightTestArgs, TestInfo, expect, request } from "@playwright/test";
22
import { TeamMediaType, TeamId, ExternalTeamViewSettings, Widget, ContestInfo } from "../../generated/api"
3-
import * as fs from "node:fs";
4-
import * as path from "node:path";
5-
6-
const BACKEND_URL = process.env.BACKEND_URL ?? "http://localhost:8080";
3+
import { BACKEND_URL, contestInfo } from "./test";
74

85
const getTeamViewSettings = async (teamId: TeamId, media: TeamMediaType) => {
96
const adminApiContext = await request.newContext({ baseURL: `${BACKEND_URL}/api/admin/` });
@@ -44,11 +41,7 @@ const testTeamViewOneMedia = (teamId: TeamId, media: TeamMediaType) =>
4441
});
4542
};
4643

47-
48-
const contestInfo = JSON.parse(fs.readFileSync(path.join(__dirname, "contestInfo.json")).toString("utf-8")) as ContestInfo;
49-
5044
test.describe("TeamViews", async () => {
51-
// const contestInfo = (await contestInfoRequest.json()) as ContestInfo;
5245
const medias = [TeamMediaType.camera, TeamMediaType.screen];
5346
for (let media of medias) {
5447
for (let team of contestInfo.teams) {
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { ContestInfo } from "../../generated/api"
2+
import WebSocket from 'ws';
3+
import * as fs from 'node:fs';
4+
import * as path from 'node:path';
5+
6+
export const BACKEND_URL = process.env.BACKEND_URL ?? "http://localhost:8080";
7+
8+
export const contestInfo = JSON.parse(fs.readFileSync(path.join(__dirname, "contestInfo.json")).toString("utf-8")) as ContestInfo;

src/frontend/overlay/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"web-vitals": "^1.1.2"
3838
},
3939
"scripts": {
40-
"start": "vite",
40+
"start": "VITE_WEBSOCKET_URL=ws://localhost:8080/api/overlay vite",
4141
"build": "vite build",
4242
"lint:css": "stylelint -f verbose './src/**/*.{js,jsx,ts,tsx,css,scss}'",
4343
"lint:js": "eslint \"**/*.{js,jsx,ts,tsx}\"",

0 commit comments

Comments
 (0)