Remove client

This commit is contained in:
2026-01-09 18:43:33 +01:00
parent 96b1cf501c
commit 0b5488be00
19 changed files with 3 additions and 4750 deletions
-54
View File
@@ -33,60 +33,6 @@ jobs:
- name: Build and test - name: Build and test
run: mvn -B verify run: mvn -B verify
electron-build:
timeout-minutes: 10
name: Electron packages (${{ matrix.os }})
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
build-command: npx electron-builder --linux AppImage --publish never
artifact-name: electron-linux
- os: windows-latest
build-command: npx electron-builder --win nsis --publish never
artifact-name: electron-windows
- os: macos-latest
build-command: npx electron-builder --mac --publish never
artifact-name: electron-macos
steps:
- uses: actions/checkout@v4
with:
lfs: true
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: npm
cache-dependency-path: package-lock.json
- name: Install dependencies
run: npm ci
- name: Build Electron bundles
run: ${{ matrix.build-command }}
- name: Smoke test AppImage
if: matrix.os == 'ubuntu-latest'
run: |
chmod +x dist/*.AppImage
./dist/*.AppImage --appimage-extract
sudo apt-get update
sudo apt-get install -y xvfb
cd squashfs-root
APPDIR="$(pwd)" xvfb-run ./AppRun --no-sandbox
- name: Upload Electron artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
path: |
dist/*.AppImage
dist/*.exe
dist/*.dmg
docker: docker:
timeout-minutes: 10 timeout-minutes: 10
runs-on: ubuntu-latest runs-on: ubuntu-latest
-41
View File
@@ -1,41 +0,0 @@
name: Manual Release
on:
workflow_dispatch:
permissions:
contents: write
jobs:
release:
name: Release (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
args: --linux AppImage
- os: windows-latest
args: --win nsis
- os: macos-latest
args: --mac
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
lfs: true
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install deps
run: npm ci
- name: Verify version is set
run: |
node -e "process.exit(require('./package.json').version ? 0 : 1)"
- name: Build + publish
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
npx electron-builder ${{ matrix.args }} --publish always
-1
View File
@@ -10,7 +10,6 @@ local/
*.db-shm *.db-shm
*.db-wal *.db-wal
previews/ previews/
node_modules/
src/main/node/dist/ src/main/node/dist/
/assets /assets
!src/main/resources/assets/ !src/main/resources/assets/
+2 -25
View File
@@ -4,19 +4,13 @@
.DEFAULT_GOAL := build .DEFAULT_GOAL := build
IMGFLOAT_DB_PATH ?= ./imgfloat.db IMGFLOAT_DB_PATH ?= ./imgfloat.db
IMGFLOAT_GITHUB_OWNER ?= Kruhlmann IMGFLOAT_GITHUB_OWNER ?= imgfloat
IMGFLOAT_GITHUB_REPO ?= imgfloat-j IMGFLOAT_GITHUB_REPO ?= client
IMGFLOAT_ASSETS_PATH ?= ./assets IMGFLOAT_ASSETS_PATH ?= ./assets
IMGFLOAT_PREVIEWS_PATH ?= ./previews IMGFLOAT_PREVIEWS_PATH ?= ./previews
SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE ?= 10MB SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE ?= 10MB
SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE ?= 10MB SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE ?= 10MB
WATCHDIR = ./src/main WATCHDIR = ./src/main
ELECTRON := $(shell \
if [ -f /etc/os-release ] && grep -q '^ID=nixos' /etc/os-release; then \
echo electron; \
else \
echo "npx electron"; \
fi)
RUNTIME_ENV = IMGFLOAT_ASSETS_PATH=$(IMGFLOAT_ASSETS_PATH) \ RUNTIME_ENV = IMGFLOAT_ASSETS_PATH=$(IMGFLOAT_ASSETS_PATH) \
IMGFLOAT_PREVIEWS_PATH=$(IMGFLOAT_PREVIEWS_PATH) \ IMGFLOAT_PREVIEWS_PATH=$(IMGFLOAT_PREVIEWS_PATH) \
IMGFLOAT_GITHUB_OWNER=$(IMGFLOAT_GITHUB_OWNER) \ IMGFLOAT_GITHUB_OWNER=$(IMGFLOAT_GITHUB_OWNER) \
@@ -48,20 +42,3 @@ test:
.PHONY: package .PHONY: package
package: package:
mvn clean package mvn clean package
.PHONY: run-client
run-client:
IMGFLOAT_CHANNELS_URL=http://localhost:8080/channels $(ELECTRON) ./src/main/node/app.js
.PHONY: run-client-x
run-client-x:
IMGFLOAT_CHANNELS_URL=http://localhost:8080/channels ./src/main/shell/run-electron-app-in-xorg $(ELECTRON)
.PHONY: run-client-wl
run-client-wl:
IMGFLOAT_CHANNELS_URL=http://localhost:8080/channels ./src/main/shell/run-electron-app-in-xorg $(ELECTRON)
.PHONY: fix
fix: node_modules
./node_modules/.bin/prettier --write src
+1 -17
View File
@@ -1,20 +1,4 @@
<p align="center"> # Server
<a href="https://imgfloat.kruhlmann.dev"><img src="src/main/resources/assets/banner.png" /></a>
</p>
[demo.webm](https://github.com/user-attachments/assets/f154ed72-6e3d-40ed-a111-706f6a916d52)
Bring your stream to life with a lightweight, real-time overlay system built for Twitch. Upload images once, place and animate them from anywhere, and see your changes live, so your stream visuals look polished.
# Getting started
## Streamers
Visit [imgfloat.kruhlmann.dev](https://imgfloat.kruhlmann.dev) to add your channel admins from the dashboard and download the application for your platform. Run the appliation, enter your twitch channel name and add the window to your OBS scene.
## Moderators
Visit [imgfloat.kruhlmann.dev](https://imgfloat.kruhlmann.dev) once your streamer has added you as a channel admin. From there you can upload images, place them on the canvas and animate them in real-time.
# Contributing # Contributing
-4301
View File
File diff suppressed because it is too large Load Diff
-66
View File
@@ -1,66 +0,0 @@
{
"name": "imgfloat-electron",
"version": "1.0.1",
"description": "Electron wrapper for the Imgfloat overlay",
"main": "src/main/node/app.js",
"build": {
"appId": "dev.kruhlmann.imgfloat",
"productName": "Imgfloat",
"files": [
"src/main/node/app.js",
"src/main/resources/**"
],
"publish": {
"provider": "github",
"owner": "Kruhlmann",
"repo": "imgfloat-j"
},
"asar": false,
"directories": {
"output": "dist"
},
"linux": {
"target": [
"AppImage"
],
"category": "Utility",
"icon": "src/main/resources/assets/icon/linux"
},
"win": {
"target": [
"nsis"
],
"icon": "src/main/resources/assets/icon/appicon.ico"
},
"nsis": {
"oneClick": true,
"perMachine": true,
"allowElevation": true,
"allowToChangeInstallationDirectory": false,
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
"shortcutName": "Imgfloat",
"installerIcon": "src/main/resources/assets/icon/appicon.ico",
"uninstallerIcon": "src/main/resources/assets/icon/appicon.ico"
},
"mac": {
"category": "public.app-category.productivity",
"icon": "src/main/resources/assets/icon/macos.icns",
"target": [
{
"target": "dmg",
"arch": ["arm64"]
}
]
}
},
"dependencies": {
"electron-updater": "^6.6.2"
},
"devDependencies": {
"electron": "^35.7.5",
"electron-builder": "^24.13.3",
"prettier": "^3.7.4",
"prettier-plugin-java": "^2.7.7"
}
}
-111
View File
@@ -1,111 +0,0 @@
const path = require("node:path");
const { app, BrowserWindow } = require("electron");
const { autoUpdater } = require("electron-updater");
const initialWindowWidthPx = 960;
const initialWindowHeightPx = 640;
let canvasSizeInterval;
function clearCanvasSizeInterval() {
if (canvasSizeInterval) {
clearInterval(canvasSizeInterval);
canvasSizeInterval = undefined;
}
}
async function autoResizeWindow(win, lastSize) {
if (win.isDestroyed()) {
return lastSize;
}
const newSize = await win.webContents.executeJavaScript(`(() => {
const canvas = document.getElementById('broadcast-canvas');
if (!canvas) {
return null;
}
const rect = canvas.getBoundingClientRect();
return {
width: Math.round(rect.width),
height: Math.round(rect.height),
};
})();`);
if (!newSize?.width || !newSize?.height) {
return lastSize;
}
if (lastSize.width === newSize.width && lastSize.height === newSize.height) {
return lastSize;
}
console.info(
`Window size did not match canvas old: ${lastSize.width}x${lastSize.height} new: ${newSize.width}x${newSize.height}. Resizing.`,
);
win.setContentSize(newSize.width, newSize.height, false);
win.setResizable(false);
return newSize;
}
function onPostNavigationLoad(win, url, broadcastRect) {
url = url || win.webContents.getURL();
let pathname;
try {
pathname = new URL(url).pathname;
} catch (e) {
console.error(`Failed to parse URL: ${url}`, e);
return;
}
const isBroadcast = /\/view\/[^/]+\/broadcast\/?$/.test(pathname);
console.info(`Navigation to ${url} detected. Is broadcast: ${isBroadcast}`);
if (isBroadcast) {
clearCanvasSizeInterval();
console.info("Setting up auto-resize for broadcast window.");
canvasSizeInterval = setInterval(() => {
autoResizeWindow(win, broadcastRect).then((newSize) => {
broadcastRect = newSize;
});
}, 750);
autoResizeWindow(win, broadcastRect).then((newSize) => {
broadcastRect = newSize;
});
} else {
clearCanvasSizeInterval();
win.setSize(initialWindowWidthPx, initialWindowHeightPx, false);
}
}
function createWindow(version) {
const win = new BrowserWindow({
width: initialWindowWidthPx,
height: initialWindowHeightPx,
transparent: true,
frame: true,
alwaysOnTop: false,
icon: path.join(__dirname, "../resources/assets/icon/appicon.ico"),
webPreferences: { backgroundThrottling: false },
});
win.setMenu(null);
win.setTitle(`Imgfloat Client v${version}`);
return win;
}
app.whenReady().then(() => {
if (process.env.CI) {
process.on("uncaughtException", (err) => {
console.error("Uncaught exception:", err);
app.exit(1);
});
setTimeout(() => app.quit(), 3000);
}
autoUpdater.checkForUpdatesAndNotify();
let broadcastRect = { width: 0, height: 0 };
const version = app.getVersion();
const win = createWindow(version);
win.loadURL(process.env["IMGFLOAT_CHANNELS_URL"] || "https://imgfloat.kruhlmann.dev/channels");
win.webContents.on("did-finish-load", () => onPostNavigationLoad(win, undefined, broadcastRect));
win.webContents.on("did-navigate", (_, url) => onPostNavigationLoad(win, url, broadcastRect));
win.webContents.on("did-navigate-in-page", (_, url) => onPostNavigationLoad(win, url, broadcastRect));
win.on("page-title-updated", (e) => e.preventDefault());
win.on("closed", clearCanvasSizeInterval);
});
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,49 +0,0 @@
#!/usr/bin/env sh
set -eu
ELECTRON="$1"
APP_ENTRY="src/main/node/app.js"
SIZE="1280x800"
RUNTIME="${XDG_RUNTIME_DIR:-/tmp}"
# Find free WAYLAND_DISPLAY
for d in 1 2 3 4 5; do
if [ ! -S "$RUNTIME/wayland-$d" ]; then
WAYLAND_NUM="$d"
break
fi
done
[ -n "${WAYLAND_NUM:-}" ] || {
echo "No free WAYLAND_DISPLAY found" >&2
exit 1
}
export WAYLAND_DISPLAY="wayland-$WAYLAND_NUM"
export XDG_RUNTIME_DIR="$RUNTIME"
cleanup() {
kill "$ELECTRON_PID" "$WESTON_PID" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
weston \
--backend=wayland-backend.so \
--width="${SIZE%x*}" \
--height="${SIZE#*x}" \
--xwayland \
--socket="$WAYLAND_DISPLAY" &
WESTON_PID=$!
sleep 1
WAYLAND_DISPLAY="$WAYLAND_DISPLAY" \
DISPLAY="" \
"${ELECTRON}" "$APP_ENTRY" &
ELECTRON_PID=$!
while :; do
kill -0 "$ELECTRON_PID" 2>/dev/null || break
kill -0 "$WESTON_PID" 2>/dev/null || break
sleep 0.5
done
-58
View File
@@ -1,58 +0,0 @@
#!/usr/bin/env sh
set -eu
ELECTRON="$1"
APP_ENTRY="src/main/node/app.js"
SCREEN="1280x800"
for d in 99 98 97 96 95; do
if ! xdpyinfo -display ":$d" >/dev/null 2>&1; then
DISP="$d"
break
fi
done
[ -n "${DISP:-}" ] || {
echo "No free DISPLAY found" >&2
exit 1
}
echo "Using DISPLAY=:$DISP"
cleanup() {
kill "$ELECTRON_PID" "$OPENBOX_PID" "$XEPHYR_PID" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
Xephyr ":$DISP" -screen "$SCREEN" -resizeable -ac &
XEPHYR_PID=$!
sleep 1
DISPLAY=":$DISP" openbox &
OPENBOX_PID=$!
sleep 0.5
DISPLAY=":$DISP" "${ELECTRON}" "$APP_ENTRY" &
ELECTRON_PID=$!
DISPLAY=":$DISP" xsetroot -solid "#009999"
while :; do
if ! kill -0 "$ELECTRON_PID" 2>/dev/null; then
echo "Electron exited — killing Xephyr"
kill "$XEPHYR_PID" 2>/dev/null || true
break
fi
if ! kill -0 "$XEPHYR_PID" 2>/dev/null; then
echo "Xephyr exited — killing Electron"
kill "$ELECTRON_PID" 2>/dev/null || true
break
fi
sleep 0.5
done
wait "$ELECTRON_PID" 2>/dev/null || true
wait "$XEPHYR_PID" 2>/dev/null || true