feat: add native fnOS package
This commit is contained in:
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
exit 0
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
exit 0
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
PASSWORD="${wizard_admin_password:-}"
|
||||
PASSWORD_CONFIRM="${wizard_admin_password_confirm:-}"
|
||||
unset wizard_admin_password wizard_admin_password_confirm
|
||||
|
||||
fail() {
|
||||
printf '%s\n' "$1" >&2
|
||||
if [ -n "${TRIM_TEMP_LOGFILE:-}" ]; then
|
||||
printf '%s\n' "$1" >>"$TRIM_TEMP_LOGFILE" 2>/dev/null || true
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ "$PASSWORD" = "$PASSWORD_CONFIRM" ] || fail "管理员密码两次输入不一致。"
|
||||
[ "${#PASSWORD}" -ge 10 ] || fail "管理员密码至少需要 10 个字符。"
|
||||
[ "${#PASSWORD}" -le 256 ] || fail "管理员密码不能超过 256 个字符。"
|
||||
case "$PASSWORD" in
|
||||
*$'\n'*|*$'\r'*) fail "管理员密码不能包含换行符。" ;;
|
||||
esac
|
||||
|
||||
mkdir -p "${TRIM_PKGVAR}/run" "${TRIM_PKGVAR}/log"
|
||||
chmod 0700 "${TRIM_PKGVAR}" "${TRIM_PKGVAR}/run" 2>/dev/null || true
|
||||
umask 077
|
||||
printf '%s\n' "$PASSWORD" >"${TRIM_PKGVAR}/admin-password.seed"
|
||||
unset PASSWORD PASSWORD_CONFIRM
|
||||
exit 0
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
exit 0
|
||||
Executable
+214
@@ -0,0 +1,214 @@
|
||||
#!/bin/bash
|
||||
set -u
|
||||
|
||||
PACKAGE_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
APP_ROOT="${TRIM_APPDEST:-$PACKAGE_ROOT}"
|
||||
DATA_ROOT="${TRIM_PKGVAR:-$PACKAGE_ROOT/var}"
|
||||
VOLUME_ROOT="${TRIM_APPDEST_VOL:-$DATA_ROOT/volume}"
|
||||
RECORD_ROOT="${LIVE_RECORDER_RECORD_ROOT:-$VOLUME_ROOT/@appshare/liverecorder/records}"
|
||||
RUNTIME_ROOT="$APP_ROOT/runtime"
|
||||
SERVER="$APP_ROOT/server/LiveRecorder.WebApi"
|
||||
PG_BIN="$RUNTIME_ROOT/usr/lib/postgresql/15/bin"
|
||||
PG_SHARE="$RUNTIME_ROOT/usr/share/postgresql/15"
|
||||
PG_DATA="$DATA_ROOT/postgres"
|
||||
RUN_ROOT="$DATA_ROOT/run"
|
||||
LOG_ROOT="$DATA_ROOT/log"
|
||||
APP_PID_FILE="$RUN_ROOT/liverecorder.pid"
|
||||
APP_LOG="$LOG_ROOT/liverecorder.log"
|
||||
PG_LOG="$LOG_ROOT/postgresql.log"
|
||||
ADMIN_PASSWORD_FILE="$DATA_ROOT/admin-password.seed"
|
||||
PG_PORT="${LIVE_RECORDER_POSTGRES_PORT:-54329}"
|
||||
SERVICE_PORT="${TRIM_SERVICE_PORT:-18080}"
|
||||
RUNTIME_LIBRARY_PATH="$RUNTIME_ROOT/lib/x86_64-linux-gnu:$RUNTIME_ROOT/usr/lib/x86_64-linux-gnu:$RUNTIME_ROOT/usr/lib/x86_64-linux-gnu/pulseaudio:$RUNTIME_ROOT/usr/lib/x86_64-linux-gnu/blas:$RUNTIME_ROOT/usr/lib/x86_64-linux-gnu/lapack:$RUNTIME_ROOT/usr/lib/postgresql/15/lib"
|
||||
|
||||
log_message() {
|
||||
mkdir -p "$LOG_ROOT"
|
||||
printf '%s - %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" >>"$APP_LOG"
|
||||
}
|
||||
|
||||
app_pid() {
|
||||
if [ -f "$APP_PID_FILE" ]; then
|
||||
pid=$(sed -n '1p' "$APP_PID_FILE" | tr -d '[:space:]')
|
||||
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
||||
printf '%s' "$pid"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
run_pg() {
|
||||
env LD_LIBRARY_PATH="$RUNTIME_LIBRARY_PATH" PATH="$PG_BIN:$RUNTIME_ROOT/usr/bin:/usr/bin:/bin" "$@"
|
||||
}
|
||||
|
||||
run_native() {
|
||||
env LD_LIBRARY_PATH="$RUNTIME_LIBRARY_PATH" PATH="$RUNTIME_ROOT/usr/bin:/usr/bin:/bin" "$@"
|
||||
}
|
||||
|
||||
postgres_running() {
|
||||
[ -x "$PG_BIN/pg_ctl" ] || return 1
|
||||
[ -f "$PG_DATA/PG_VERSION" ] || return 1
|
||||
run_pg "$PG_BIN/pg_ctl" -D "$PG_DATA" status >/dev/null 2>&1
|
||||
}
|
||||
|
||||
initialize_postgres() {
|
||||
if [ -f "$PG_DATA/PG_VERSION" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
mkdir -p "$PG_DATA" "$RUN_ROOT"
|
||||
chmod 0700 "$PG_DATA" "$RUN_ROOT"
|
||||
if ! run_pg "$PG_BIN/initdb" \
|
||||
-D "$PG_DATA" \
|
||||
-L "$PG_SHARE" \
|
||||
--username=liverecorder \
|
||||
--auth-local=trust \
|
||||
--auth-host=reject \
|
||||
--encoding=UTF8 \
|
||||
--no-locale >>"$PG_LOG" 2>&1; then
|
||||
log_message "PostgreSQL 初始化失败。"
|
||||
return 1
|
||||
fi
|
||||
|
||||
{
|
||||
printf "listen_addresses = ''\n"
|
||||
printf "port = %s\n" "$PG_PORT"
|
||||
printf "unix_socket_directories = '%s'\n" "$RUN_ROOT"
|
||||
printf "max_connections = 40\n"
|
||||
printf "shared_buffers = '64MB'\n"
|
||||
printf "timezone = 'UTC'\n"
|
||||
printf "log_timezone = 'UTC'\n"
|
||||
} >>"$PG_DATA/postgresql.conf"
|
||||
return 0
|
||||
}
|
||||
|
||||
start_postgres() {
|
||||
if postgres_running; then
|
||||
return 0
|
||||
fi
|
||||
initialize_postgres || return 1
|
||||
if ! run_pg "$PG_BIN/pg_ctl" -D "$PG_DATA" -l "$PG_LOG" -w start; then
|
||||
log_message "PostgreSQL 启动失败。"
|
||||
return 1
|
||||
fi
|
||||
|
||||
database_exists=$(run_pg "$PG_BIN/psql" \
|
||||
-h "$RUN_ROOT" -p "$PG_PORT" -U liverecorder -d postgres -Atqc \
|
||||
"SELECT 1 FROM pg_database WHERE datname = 'live_recorder'" 2>/dev/null || true)
|
||||
if [ "$database_exists" != "1" ]; then
|
||||
if ! run_pg "$PG_BIN/createdb" \
|
||||
-h "$RUN_ROOT" -p "$PG_PORT" -U liverecorder live_recorder >>"$PG_LOG" 2>&1; then
|
||||
log_message "Live Recorder 数据库创建失败。"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
stop_postgres() {
|
||||
if postgres_running; then
|
||||
run_pg "$PG_BIN/pg_ctl" -D "$PG_DATA" -m fast -w stop >>"$PG_LOG" 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
launch_app_process() {
|
||||
(
|
||||
export ASPNETCORE_ENVIRONMENT=Production
|
||||
export ASPNETCORE_URLS="http://0.0.0.0:$SERVICE_PORT"
|
||||
export ConnectionStrings__DefaultConnection="Host=$RUN_ROOT;Port=$PG_PORT;Database=live_recorder;Username=liverecorder;Timeout=15;Command Timeout=120;Keepalive=30"
|
||||
export LIVE_RECORDER_DEFAULT_OUTPUT_ROOT="$RECORD_ROOT"
|
||||
if [ -f "$ADMIN_PASSWORD_FILE" ]; then
|
||||
LIVE_RECORDER_DEFAULT_ADMIN_PASSWORD=$(sed -n '1p' "$ADMIN_PASSWORD_FILE")
|
||||
export LIVE_RECORDER_DEFAULT_ADMIN_PASSWORD
|
||||
fi
|
||||
export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
|
||||
export DOTNET_BUNDLE_EXTRACT_BASE_DIR="$DATA_ROOT/dotnet-bundle"
|
||||
export XDG_CACHE_HOME="$DATA_ROOT/cache"
|
||||
export TMPDIR="$DATA_ROOT/tmp"
|
||||
export LD_LIBRARY_PATH="$RUNTIME_LIBRARY_PATH"
|
||||
export PATH="$RUNTIME_ROOT/usr/bin:/usr/bin:/bin"
|
||||
mkdir -p "$XDG_CACHE_HOME" "$TMPDIR"
|
||||
cd "$APP_ROOT/server" || exit 1
|
||||
exec "$SERVER"
|
||||
) >>"$APP_LOG" 2>&1 &
|
||||
APP_PROCESS_PID=$!
|
||||
printf '%s\n' "$APP_PROCESS_PID" >"$APP_PID_FILE"
|
||||
}
|
||||
|
||||
start_app() {
|
||||
mkdir -p "$DATA_ROOT" "$RUN_ROOT" "$LOG_ROOT" "$RECORD_ROOT" "$DATA_ROOT/dotnet-bundle"
|
||||
chmod 0700 "$DATA_ROOT" "$RUN_ROOT" "$DATA_ROOT/dotnet-bundle" 2>/dev/null || true
|
||||
|
||||
if [ ! -x "$SERVER" ]; then
|
||||
log_message "应用程序不存在或不可执行:$SERVER"
|
||||
return 1
|
||||
fi
|
||||
if [ ! -x "$PG_BIN/postgres" ] || [ ! -x "$RUNTIME_ROOT/usr/bin/ffmpeg" ] || [ ! -x "$RUNTIME_ROOT/usr/bin/node" ]; then
|
||||
log_message "FPK 原生运行环境不完整。"
|
||||
return 1
|
||||
fi
|
||||
if pid=$(app_pid); then
|
||||
log_message "应用已运行,PID $pid。"
|
||||
return 0
|
||||
fi
|
||||
|
||||
rm -f "$APP_PID_FILE"
|
||||
start_postgres || return 1
|
||||
launch_attempt=1
|
||||
launch_app_process
|
||||
pid=$APP_PROCESS_PID
|
||||
|
||||
attempt=0
|
||||
while [ "$attempt" -lt 90 ]; do
|
||||
if run_native "$RUNTIME_ROOT/usr/bin/curl" -fsS "http://127.0.0.1:$SERVICE_PORT/health/ready" >/dev/null 2>&1; then
|
||||
rm -f "$ADMIN_PASSWORD_FILE"
|
||||
log_message "应用启动成功,PID $pid,端口 $SERVICE_PORT。"
|
||||
return 0
|
||||
fi
|
||||
if ! kill -0 "$pid" 2>/dev/null; then
|
||||
if [ "$launch_attempt" -ge 3 ]; then
|
||||
break
|
||||
fi
|
||||
log_message "应用启动进程提前退出,3 秒后重试($launch_attempt/3)。"
|
||||
sleep 3
|
||||
launch_attempt=$((launch_attempt + 1))
|
||||
launch_app_process
|
||||
pid=$APP_PROCESS_PID
|
||||
fi
|
||||
attempt=$((attempt + 1))
|
||||
sleep 1
|
||||
done
|
||||
|
||||
log_message "应用未能在 90 秒内就绪。"
|
||||
stop_app
|
||||
return 1
|
||||
}
|
||||
|
||||
stop_app() {
|
||||
if pid=$(app_pid); then
|
||||
kill "$pid" 2>/dev/null || true
|
||||
attempt=0
|
||||
while kill -0 "$pid" 2>/dev/null && [ "$attempt" -lt 30 ]; do
|
||||
sleep 1
|
||||
attempt=$((attempt + 1))
|
||||
done
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
kill -9 "$pid" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
rm -f "$APP_PID_FILE"
|
||||
stop_postgres
|
||||
}
|
||||
|
||||
case "${1:-status}" in
|
||||
start) start_app ;;
|
||||
stop) stop_app ;;
|
||||
restart) stop_app; start_app ;;
|
||||
status)
|
||||
if app_pid >/dev/null && postgres_running; then
|
||||
exit 0
|
||||
fi
|
||||
exit 3
|
||||
;;
|
||||
*) printf 'usage: %s {start|stop|restart|status}\n' "$0" >&2; exit 2 ;;
|
||||
esac
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
# Persistent PostgreSQL data and recordings are preserved by default.
|
||||
exit 0
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
"$(dirname -- "$0")/main" stop || true
|
||||
exit 0
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
exit 0
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
# PostgreSQL data, logs and application settings live in TRIM_PKGVAR and are
|
||||
# intentionally not touched while fnOS replaces the immutable application.
|
||||
exit 0
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"defaults": {
|
||||
"run-as": "package"
|
||||
},
|
||||
"join-groups": ["video"]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"data-share": {
|
||||
"shares": [
|
||||
{
|
||||
"name": "liverecorder",
|
||||
"permission": {
|
||||
"rw": ["liverecorder"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "liverecorder/records",
|
||||
"permission": {
|
||||
"rw": ["liverecorder"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
appname=liverecorder
|
||||
version=1.0.0
|
||||
display_name=Live Recorder
|
||||
desc=原生自包含直播录制系统,内置 PostgreSQL、FFmpeg、Node.js 和 Web 管理界面,支持分片录制、弹幕采集与 OpenList 自动上传
|
||||
platform=x86
|
||||
source=thirdparty
|
||||
maintainer=Live Recorder Contributors
|
||||
os_min_version=1.2.0
|
||||
desktop_uidir=ui
|
||||
desktop_applaunchname=liverecorder.Application
|
||||
checksum=@CHECKSUM@
|
||||
checkport=true
|
||||
ctl_stop=true
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
".url": {
|
||||
"liverecorder.Application": {
|
||||
"title": "Live Recorder",
|
||||
"icon": "images/icon_{0}.png",
|
||||
"type": "url",
|
||||
"protocol": "",
|
||||
"port": "18080",
|
||||
"url": "/",
|
||||
"allUsers": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
[
|
||||
{
|
||||
"stepTitle": "设置 Live Recorder 管理员密码",
|
||||
"items": [
|
||||
{
|
||||
"type": "tips",
|
||||
"helpText": "此密码用于登录 Live Recorder,不是 fnOS 系统密码。用户名固定为 admin。"
|
||||
},
|
||||
{
|
||||
"type": "password",
|
||||
"field": "wizard_admin_password",
|
||||
"label": "管理员密码",
|
||||
"rules": [
|
||||
{
|
||||
"required": true,
|
||||
"message": "请输入管理员密码"
|
||||
},
|
||||
{
|
||||
"min": 10,
|
||||
"message": "管理员密码至少需要 10 个字符"
|
||||
},
|
||||
{
|
||||
"max": 256,
|
||||
"message": "管理员密码不能超过 256 个字符"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "password",
|
||||
"field": "wizard_admin_password_confirm",
|
||||
"label": "再次输入密码",
|
||||
"rules": [
|
||||
{
|
||||
"required": true,
|
||||
"message": "请再次输入管理员密码"
|
||||
},
|
||||
{
|
||||
"min": 10,
|
||||
"message": "管理员密码至少需要 10 个字符"
|
||||
},
|
||||
{
|
||||
"max": 256,
|
||||
"message": "管理员密码不能超过 256 个字符"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,11 @@
|
||||
[
|
||||
{
|
||||
"stepTitle": "升级 Live Recorder",
|
||||
"items": [
|
||||
{
|
||||
"type": "tips",
|
||||
"helpText": "升级会保留 PostgreSQL 数据库、系统设置、上传任务和录制文件。"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user