Skip to content

Post WF content gen #213

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ If you don't need auth, you can disable it by `--no-auth` command-line option.

# Run in development mode

[Check separate document](https://github.com/icpc/live-v3/blob/main/docs/development.md)
[Check separate document](./docs/development.md)

# Previous versions:

Expand Down
2 changes: 1 addition & 1 deletion docs/frontend-tricks.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ Examples:
- `http://host/overlay?onlyWidgets=queue`
show only queue
- `http://host/overlay?onlyWidgets=teamview.BOTTOM_RIGHT,teamview.BOTTOM_LEFT`
show only bottom teamviews
show only bottom teamviews
33 changes: 33 additions & 0 deletions icpc-live-v3.code-workspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"folders": [
{
"name": "✨ root",
"path": "."
},
{
"name": "🚀 icpc-live-v3",
"path": "src/frontend"
},
{
"name": "🚀 icpc-live-v3-admin",
"path": "src/frontend/admin"
},
{
"name": "🚀 shared-code",
"path": "src/frontend/common"
},
{
"name": "🚀 content-gen",
"path": "src/frontend/content-gen"
},
{
"name": "🚀 icpc-live-v3-locator",
"path": "src/frontend/locator"
},
{
"name": "🚀 icpc-live-v3-overlay",
"path": "src/frontend/overlay"
}
],
"settings": {}
}
2 changes: 2 additions & 0 deletions src/frontend/content-gen/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ tests/screenshots/
playwright-report/
blob-report/
playwright/.cache/
videos
config
108 changes: 108 additions & 0 deletions src/frontend/content-gen/gen_video.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#!/bin/bash
set -e
set -o pipefail
set -x

OFFSET=4
SPEEDUP=15
CONTEST_NAME="vkoshp"
REPO_ROOT=$(git rev-parse --show-toplevel)

# echo "Step 1: Download latest event-feed.json"
# rm -f event-feed-$CONTEST_NAME.ndjson
# 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

# echo "Step 2: Edit the config for backend"
# cp $REPO_ROOT/artifacts/live-v3-dev.jar ./
# rm -rf config
# rsync -arv --exclude ach --exclude advanced.json /Volumes/C\$/work/overlay/config/ config

# mv event-feed-$CONTEST_NAME.ndjson config/$CONTEST_NAME/event-feed.ndjson
# we do a little trolling
# cp config/systest2_replay/event-feed-systest2.ndjson config/$CONTEST_NAME/event-feed.ndjson

# jq '.feeds[0].source = "."' config/$CONTEST_NAME/settings.json > config/$CONTEST_NAME/settings.json.tmp
# mv config/$CONTEST_NAME/settings.json.tmp config/$CONTEST_NAME/settings.json

startTime=$(python3 - <<EOF
import datetime
delta=datetime.timedelta(hours=$OFFSET)
padding = datetime.timedelta(seconds=15)
print((datetime.datetime.now(datetime.timezone.utc).astimezone() - delta / $SPEEDUP + padding).isoformat())
EOF
)
echo "Start time: $startTime"

cat <<EOF > config/$CONTEST_NAME/settings.json
{
"type": "pcms",
"network": { "allowUnsecureConnections": true },
"source": "runs.xml",
"emulation": {
"speed": $SPEEDUP,
"startTime": "$startTime",
}
}
EOF

echo "Step 3: add ticker messages"
mkdir config/$CONTEST_NAME/presets || true
cat <<EOF > config/$CONTEST_NAME/presets/ticker.json
[
{
"type": "text",
"part": "long",
"periodMs": 30000,
"text": "REPLAY"
},
{
"type": "text",
"part": "short",
"periodMs": 30000,
"text": "ICPCLive"
}
]
EOF

echo "Step 4: Start backend"
java -jar live-v3-dev.jar -c config/$CONTEST_NAME --no-auth > ./backend.log &
BACKEND_PID=$!
function cleanup {
echo "Step INF: Cleanup"
kill $BACKEND_PID
wait $BACKEND_PID || true
}
trap cleanup EXIT
sleep 5

echo "Step 5: Show ticker messages"
curl -X POST 'http://localhost:8080/api/admin/tickerMessage/1/show' -v
curl -X POST 'http://localhost:8080/api/admin/tickerMessage/2/show' -v

echo "Step 6: Start video generation"
stopwatch() {
start=$(gdate +%s)
while true; do
time="$(( $(gdate +%s) - $start ))"
printf '%s\r' "$(gdate -u -d "@$time" +%H:%M:%S)"
sleep 0.1
done
}
set +x
stopwatch &
STOPWATCH_PID=$!
./node_modules/.bin/playwright test tests/story.spec.ts
kill $STOPWATCH_PID


echo "Step 7: Reencode to mp4 60fps and speed up the result video"
lastVideo=$(ls -t videos/*.webm | head -n1)
echo "Last video: $lastVideo"
targetLength=55
speedup=$(echo "scale=4; (60 / $SPEEDUP) / ($targetLength / 60)" | bc)
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
targetLength=12
speedup=$(echo "scale=4; (60 / $SPEEDUP) / ($targetLength / 60)" | bc)
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


17 changes: 17 additions & 0 deletions src/frontend/content-gen/generateContestInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use strict";

import WebSocket from "ws";
import * as fs from "node:fs"
import * as path from "node:path";
export const BACKEND_URL = process.env.BACKEND_URL ?? "http://localhost:8080";

const contestInfo = await new Promise((resolve, reject) => {
console.log("Opening websocket")
const websocket = new WebSocket(`${BACKEND_URL.replace('http', 'ws')}/api/overlay/contestInfo`)
websocket.onmessage = (message) => {
resolve(JSON.parse(message.data));
websocket.close()
}
})

fs.writeFileSync("contestInfo.json", JSON.stringify(contestInfo))
74 changes: 0 additions & 74 deletions src/frontend/content-gen/package-lock.json

This file was deleted.

8 changes: 6 additions & 2 deletions src/frontend/content-gen/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@
"author": "",
"license": "ISC",
"devDependencies": {
"@playwright/test": "^1.32.3",
"@playwright/test": "^1.47.1",
"@types/node": "^22.5.4",
"fs": "0.0.1-security",
"path": "^0.12.7"
"path": "^0.12.7",
"ts-node": "^10.9.2"
},
"scripts": {
"install-browsers": "playwright install --with-deps",
"teamview": "playwright test teamview.spec.ts"
},
"dependencies": {
"ws": "^8.13.0"
}
}
89 changes: 89 additions & 0 deletions src/frontend/content-gen/tests/story.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import test, { ConsoleMessage, expect } from "@playwright/test";

const minutes10 = 1000 * 60 * 10;
const WIDTH = 1080;
const HEIGHT = 1920;
const visualConfig = {
SCREEN_WIDTH: WIDTH,
SCREEN_HEIGHT: HEIGHT,
TICKER_SMALL_SIZE: "50%",
TICKER_LIVE_ICON_SIZE: "70px",
SCOREBOARD_CELL_PLACE_SIZE: "50px",
SCOREBOARD_CELL_TEAMNAME_SIZE: "304px",
SCOREBOARD_CELL_POINTS_SIZE: "50px",
SCOREBOARD_CELL_PENALTY_SIZE: "92px",
FULL_SCREEN_CLOCK_FONT_SIZE: "300px",
FULL_SCREEN_CLOCK_COLOR: "#eeeeee33",
BACKGROUND: "#691930"
};
const widgets = [
{
type: "ScoreboardWidget",
widgetId: "scoreboard",
location: {
positionX: 16,
positionY: 148,
sizeX: 1048,
sizeY: 1756,
},
statisticsId: "scoreboard",
settings: {
scrollDirection: "FirstPage",
optimismLevel: "normal",
group: "all",
},
},
{
type: "TickerWidget",
widgetId: "ticker",
location: {
positionX: 540,
positionY: 46,
sizeX: 524,
sizeY: 86,
},
statisticsId: "ticker",
settings: {},
},
{
type: "FullScreenClockWidget",
widgetId: "fullScreenClock",
location: {
positionX: 16,
positionY: 148,
sizeX: 1048,
sizeY: 1756,
},
statisticsId: "fullScreenClock",
settings: {
globalTimeMode: false,
quietMode: false,
contestCountdownMode: false,
},
},
];
test.describe("Instagram story", async () => {
test.setTimeout(minutes10);
test("Instagram story", async ({ browser }) => {
const context = await browser.newContext({
recordVideo: { dir: "videos/", size: { width: WIDTH, height: HEIGHT } },
viewport: { width: WIDTH, height: HEIGHT },
});
const page = await context.newPage();
// const messages: ConsoleMessage[] = [];
// page.on('console', m => messages.push(m));
const url = `localhost:8080/overlay?forceWidgets=${encodeURIComponent(
JSON.stringify(widgets)
)}&forceVisualConfig=${encodeURIComponent(JSON.stringify(visualConfig))}`;
console.log(url);
await page.goto(url);
const selector = await page.waitForSelector(
'[data-widget-id="scoreboard"]'
);
const locator = await page.locator('[data-widget-id="scoreboard"]');
await page.waitForTimeout(4 * 60 * 1000 + 15 * 1000);
// await expect(locator).toHaveText('OVER', { timeout: 100000000})
await context.close();
// expect(messages).toEqual([]);
});
});
9 changes: 1 addition & 8 deletions src/frontend/content-gen/tests/teamview.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import test, { PlaywrightTestArgs, TestInfo, expect, request } from "@playwright/test";
import { TeamMediaType, TeamId, ExternalTeamViewSettings, Widget, ContestInfo } from "../../generated/api"
import * as fs from "node:fs";
import * as path from "node:path";

const BACKEND_URL = process.env.BACKEND_URL ?? "http://localhost:8080";
import { BACKEND_URL, contestInfo } from "./test";

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


const contestInfo = JSON.parse(fs.readFileSync(path.join(__dirname, "contestInfo.json")).toString("utf-8")) as ContestInfo;

test.describe("TeamViews", async () => {
// const contestInfo = (await contestInfoRequest.json()) as ContestInfo;
const medias = [TeamMediaType.camera, TeamMediaType.screen];
for (let media of medias) {
for (let team of contestInfo.teams) {
Expand Down
Loading
Loading