From f76dfe8a6792ff8ee58351f639adbfc8fd40ef63 Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Sat, 21 Feb 2026 18:34:26 +0100 Subject: [PATCH] feat(peertube): Add web interface for video analysis - Create standalone web UI at /peertube-analyse/ - Add CGI backend (peertube-analyse, peertube-analyse-status) - Add RPCD methods: analyse, analyse_status - Update portal with Intelligence & Analyse section - Expose via analyse.gk2.secubox.in with SSL Co-Authored-By: Claude Opus 4.5 --- .claude/HISTORY.md | 32 + .../root/usr/libexec/rpcd/luci.peertube | 161 ++++ .../share/rpcd/acl.d/luci-app-peertube.json | 4 +- .../root/www/gk2-hub/portal.html | 14 + package/secubox/secubox-app-peertube/Makefile | 7 + .../files/www/cgi-bin/peertube-analyse | 151 ++++ .../files/www/cgi-bin/peertube-analyse-status | 64 ++ .../files/www/peertube-analyse/index.html | 762 ++++++++++++++++++ 8 files changed, 1193 insertions(+), 2 deletions(-) create mode 100644 package/secubox/secubox-app-peertube/files/www/cgi-bin/peertube-analyse create mode 100644 package/secubox/secubox-app-peertube/files/www/cgi-bin/peertube-analyse-status create mode 100644 package/secubox/secubox-app-peertube/files/www/peertube-analyse/index.html diff --git a/.claude/HISTORY.md b/.claude/HISTORY.md index cf2f6cb7..52ae8570 100644 --- a/.claude/HISTORY.md +++ b/.claude/HISTORY.md @@ -2992,3 +2992,35 @@ git checkout HEAD -- index.html - Files: - `secubox-app-peertube/files/usr/sbin/peertube-analyse` (778 lines) - `secubox-app-peertube/Makefile` (updated) + +64. **PeerTube Analyse Web Interface & Portal (2026-02-21)** + - Created standalone web interface for PeerTube video analysis. + - **URL**: https://analyse.gk2.secubox.in/peertube-analyse/ + - **Web Interface Features**: + - Cyberpunk-themed design matching SecuBox portal + - Video URL input with example presets + - Options: Force Whisper, No AI Analysis, Model/Language selection + - Progress status bar with live polling + - Tabbed results: Analysis (Markdown), Transcript, Metadata + - Copy to clipboard functionality + - **CGI Backend**: + - `/cgi-bin/peertube-analyse` — Start analysis (POST) + - `/cgi-bin/peertube-analyse-status` — Poll job status (GET) + - Async job system with background processing + - JSON API with job_id for polling + - **RPCD Integration**: + - Added `analyse` and `analyse_status` methods to `luci.peertube` + - ACL permissions updated for read/write access + - **Portal Integration**: + - New "Intelligence & Analyse" section in SecuBox portal + - Added PeerTube Analyse and Radio Stream services + - **HAProxy/SSL**: + - Domain: analyse.gk2.secubox.in + - Let's Encrypt certificate auto-provisioned + - Routing via uhttpd backend (static content) + - Files: + - `secubox-app-peertube/files/www/peertube-analyse/index.html` + - `secubox-app-peertube/files/www/cgi-bin/peertube-analyse` + - `secubox-app-peertube/files/www/cgi-bin/peertube-analyse-status` + - `luci-app-peertube/root/usr/libexec/rpcd/luci.peertube` (updated) + - `luci-app-secubox-portal/root/www/gk2-hub/portal.html` (updated) diff --git a/package/secubox/luci-app-peertube/root/usr/libexec/rpcd/luci.peertube b/package/secubox/luci-app-peertube/root/usr/libexec/rpcd/luci.peertube index 38b83b06..738a0cac 100644 --- a/package/secubox/luci-app-peertube/root/usr/libexec/rpcd/luci.peertube +++ b/package/secubox/luci-app-peertube/root/usr/libexec/rpcd/luci.peertube @@ -530,6 +530,151 @@ method_import_job_status() { json_dump } +# Method: analyse - Start video transcript analysis +method_analyse() { + read -r input + json_load "$input" + json_get_var url url "" + json_get_var force_whisper force_whisper "0" + json_get_var no_analyse no_analyse "0" + json_get_var model model "medium" + json_get_var lang lang "fr" + + if [ -z "$url" ]; then + json_init + json_add_boolean "success" 0 + json_add_string "error" "URL is required" + json_dump + return + fi + + # Check if peertube-analyse exists + if [ ! -x "/usr/sbin/peertube-analyse" ]; then + json_init + json_add_boolean "success" 0 + json_add_string "error" "peertube-analyse not installed" + json_dump + return + fi + + # Generate job ID + local job_id="analyse_$(date +%s)_$$" + local output_dir="/tmp/peertube-analyse/${job_id}" + local statusfile="/tmp/peertube-analyse-${job_id}.status" + local resultfile="/tmp/peertube-analyse-${job_id}.json" + + mkdir -p "$output_dir" + echo "starting" > "$statusfile" + + # Build command args + local args="" + [ "$force_whisper" = "1" ] && args="$args --force-whisper" + [ "$no_analyse" = "1" ] && args="$args --no-analyse" + [ -n "$model" ] && args="$args --model $model" + [ -n "$lang" ] && args="$args --lang $lang" + args="$args --output $output_dir" + + # Run in background + ( + echo "extracting" > "$statusfile" + + # Run the analysis + OUTPUT_BASE="$output_dir" /usr/sbin/peertube-analyse $args "$url" > "/tmp/peertube-analyse-${job_id}.log" 2>&1 + local rc=$? + + if [ $rc -eq 0 ]; then + echo "completed" > "$statusfile" + + # Build result JSON + local meta_file=$(find "$output_dir" -name "*.meta.json" -type f 2>/dev/null | head -1) + local transcript_file=$(find "$output_dir" -name "*.transcript.txt" -type f 2>/dev/null | head -1) + local analysis_file=$(find "$output_dir" -name "*.analyse.md" -type f 2>/dev/null | head -1) + + # Create result JSON manually (avoid jshn for large content) + { + echo '{' + echo '"success": true,' + echo '"job_id": "'"$job_id"'",' + + # Metadata (use jq if available, otherwise jsonfilter) + if [ -f "$meta_file" ]; then + echo '"metadata": ' + cat "$meta_file" + echo ',' + else + echo '"metadata": null,' + fi + + # Transcript + if [ -f "$transcript_file" ]; then + printf '"transcript": ' + cat "$transcript_file" | jq -Rs '.' + echo ',' + else + echo '"transcript": null,' + fi + + # Analysis + if [ -f "$analysis_file" ]; then + printf '"analysis": ' + cat "$analysis_file" | jq -Rs '.' + else + echo '"analysis": null' + fi + + echo '}' + } > "$resultfile" + else + echo "failed" > "$statusfile" + echo '{"success": false, "error": "Analysis failed", "job_id": "'"$job_id"'"}' > "$resultfile" + fi + ) & + + json_init + json_add_boolean "success" 1 + json_add_string "message" "Analysis started" + json_add_string "job_id" "$job_id" + json_dump +} + +# Method: analyse_status - Get analysis job status/results +method_analyse_status() { + read -r input + json_load "$input" + json_get_var job_id job_id "" + + if [ -z "$job_id" ]; then + json_init + json_add_boolean "success" 0 + json_add_string "error" "job_id is required" + json_dump + return + fi + + local statusfile="/tmp/peertube-analyse-${job_id}.status" + local resultfile="/tmp/peertube-analyse-${job_id}.json" + local logfile="/tmp/peertube-analyse-${job_id}.log" + + local status="unknown" + [ -f "$statusfile" ] && status=$(cat "$statusfile") + + # If completed, return the full result + if [ "$status" = "completed" ] && [ -f "$resultfile" ]; then + cat "$resultfile" + return + fi + + # Otherwise return status + local logs="" + [ -f "$logfile" ] && logs=$(tail -10 "$logfile") + + json_init + json_add_string "status" "$status" + json_add_string "job_id" "$job_id" + json_add_string "logs" "$logs" + json_dump +} + # Method: import_status - Check import progress method_import_status() { local import_dir="/var/lib/peertube/storage/tmp/import" @@ -589,6 +734,16 @@ list_methods() { json_add_object "import_job_status" json_add_string "job_id" "" json_close_object + json_add_object "analyse" + json_add_string "url" "" + json_add_string "force_whisper" "0" + json_add_string "no_analyse" "0" + json_add_string "model" "medium" + json_add_string "lang" "fr" + json_close_object + json_add_object "analyse_status" + json_add_string "job_id" "" + json_close_object json_dump } @@ -644,6 +799,12 @@ case "$1" in import_job_status) method_import_job_status ;; + analyse) + method_analyse + ;; + analyse_status) + method_analyse_status + ;; *) echo '{"error":"Method not found"}' ;; diff --git a/package/secubox/luci-app-peertube/root/usr/share/rpcd/acl.d/luci-app-peertube.json b/package/secubox/luci-app-peertube/root/usr/share/rpcd/acl.d/luci-app-peertube.json index 7993bed1..946feb1e 100644 --- a/package/secubox/luci-app-peertube/root/usr/share/rpcd/acl.d/luci-app-peertube.json +++ b/package/secubox/luci-app-peertube/root/usr/share/rpcd/acl.d/luci-app-peertube.json @@ -3,13 +3,13 @@ "description": "Grant access to PeerTube management", "read": { "ubus": { - "luci.peertube": ["status", "logs", "import_status", "import_job_status"] + "luci.peertube": ["status", "logs", "import_status", "import_job_status", "analyse_status"] }, "uci": ["peertube"] }, "write": { "ubus": { - "luci.peertube": ["start", "stop", "install", "uninstall", "update", "emancipate", "live_enable", "live_disable", "configure_haproxy", "import_video"] + "luci.peertube": ["start", "stop", "install", "uninstall", "update", "emancipate", "live_enable", "live_disable", "configure_haproxy", "import_video", "analyse"] }, "uci": ["peertube"] } diff --git a/package/secubox/luci-app-secubox-portal/root/www/gk2-hub/portal.html b/package/secubox/luci-app-secubox-portal/root/www/gk2-hub/portal.html index 8bfe8cbb..acf58b97 100644 --- a/package/secubox/luci-app-secubox-portal/root/www/gk2-hub/portal.html +++ b/package/secubox/luci-app-secubox-portal/root/www/gk2-hub/portal.html @@ -200,6 +200,20 @@ +

Intelligence & Analyse

+ +

Administration

diff --git a/package/secubox/secubox-app-peertube/Makefile b/package/secubox/secubox-app-peertube/Makefile index 9ea0d886..10318985 100644 --- a/package/secubox/secubox-app-peertube/Makefile +++ b/package/secubox/secubox-app-peertube/Makefile @@ -42,6 +42,13 @@ define Package/secubox-app-peertube/install $(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_BIN) ./files/usr/sbin/peertubectl $(1)/usr/sbin/peertubectl $(INSTALL_BIN) ./files/usr/sbin/peertube-analyse $(1)/usr/sbin/peertube-analyse + + $(INSTALL_DIR) $(1)/www/peertube-analyse + $(INSTALL_DATA) ./files/www/peertube-analyse/index.html $(1)/www/peertube-analyse/ + + $(INSTALL_DIR) $(1)/www/cgi-bin + $(INSTALL_BIN) ./files/www/cgi-bin/peertube-analyse $(1)/www/cgi-bin/ + $(INSTALL_BIN) ./files/www/cgi-bin/peertube-analyse-status $(1)/www/cgi-bin/ endef $(eval $(call BuildPackage,secubox-app-peertube)) diff --git a/package/secubox/secubox-app-peertube/files/www/cgi-bin/peertube-analyse b/package/secubox/secubox-app-peertube/files/www/cgi-bin/peertube-analyse new file mode 100644 index 00000000..2797c420 --- /dev/null +++ b/package/secubox/secubox-app-peertube/files/www/cgi-bin/peertube-analyse @@ -0,0 +1,151 @@ +#!/bin/sh +# CGI endpoint for PeerTube video analysis +# Returns JSON response + +# Set headers +printf "Content-Type: application/json\r\n" +printf "Access-Control-Allow-Origin: *\r\n" +printf "Access-Control-Allow-Methods: POST, OPTIONS\r\n" +printf "Access-Control-Allow-Headers: Content-Type\r\n" +printf "\r\n" + +# Handle OPTIONS (CORS preflight) +if [ "$REQUEST_METHOD" = "OPTIONS" ]; then + exit 0 +fi + +# Only allow POST +if [ "$REQUEST_METHOD" != "POST" ]; then + echo '{"error": "Method not allowed"}' + exit 0 +fi + +# Read input +INPUT=$(cat) + +# Parse JSON (use jq if available, else jsonfilter) +if command -v jq >/dev/null 2>&1; then + URL=$(echo "$INPUT" | jq -r '.url // empty') + FORCE_WHISPER=$(echo "$INPUT" | jq -r '.force_whisper // "0"') + NO_ANALYSE=$(echo "$INPUT" | jq -r '.no_analyse // "0"') + MODEL=$(echo "$INPUT" | jq -r '.model // "medium"') + LANG=$(echo "$INPUT" | jq -r '.lang // "fr"') +else + URL=$(echo "$INPUT" | jsonfilter -e '@.url' 2>/dev/null) + FORCE_WHISPER=$(echo "$INPUT" | jsonfilter -e '@.force_whisper' 2>/dev/null) + NO_ANALYSE=$(echo "$INPUT" | jsonfilter -e '@.no_analyse' 2>/dev/null) + MODEL=$(echo "$INPUT" | jsonfilter -e '@.model' 2>/dev/null) + LANG=$(echo "$INPUT" | jsonfilter -e '@.lang' 2>/dev/null) +fi + +# Validate URL +if [ -z "$URL" ]; then + echo '{"error": "URL is required"}' + exit 0 +fi + +# Sanitize URL (basic security check) +case "$URL" in + http://*|https://*) + # Valid URL prefix + ;; + *) + echo '{"error": "Invalid URL format"}' + exit 0 + ;; +esac + +# Set defaults +[ -z "$MODEL" ] && MODEL="medium" +[ -z "$LANG" ] && LANG="fr" + +# Generate job ID +JOB_ID="analyse_$(date +%s)_$$" +OUTPUT_DIR="/tmp/peertube-analyse/${JOB_ID}" +STATUS_FILE="/tmp/peertube-analyse-${JOB_ID}.status" +RESULT_FILE="/tmp/peertube-analyse-${JOB_ID}.json" +LOG_FILE="/tmp/peertube-analyse-${JOB_ID}.log" + +mkdir -p "$OUTPUT_DIR" + +# Check for existing analysis script +if [ ! -x "/usr/sbin/peertube-analyse" ]; then + echo '{"error": "peertube-analyse not installed"}' + exit 0 +fi + +# Build command arguments +ARGS="" +[ "$FORCE_WHISPER" = "1" ] || [ "$FORCE_WHISPER" = "true" ] && ARGS="$ARGS --force-whisper" +[ "$NO_ANALYSE" = "1" ] || [ "$NO_ANALYSE" = "true" ] && ARGS="$ARGS --no-analyse" +[ -n "$MODEL" ] && ARGS="$ARGS --model $MODEL" +[ -n "$LANG" ] && ARGS="$ARGS --lang $LANG" +ARGS="$ARGS --output $OUTPUT_DIR" + +# Start analysis in background +echo "starting" > "$STATUS_FILE" +( + echo "extracting" > "$STATUS_FILE" + + # Run the analysis + OUTPUT_BASE="$OUTPUT_DIR" /usr/sbin/peertube-analyse $ARGS "$URL" > "$LOG_FILE" 2>&1 + RC=$? + + if [ $RC -eq 0 ]; then + echo "completed" > "$STATUS_FILE" + + # Find output files + META_FILE=$(find "$OUTPUT_DIR" -name "*.meta.json" -type f 2>/dev/null | head -1) + TRANSCRIPT_FILE=$(find "$OUTPUT_DIR" -name "*.transcript.txt" -type f 2>/dev/null | head -1) + ANALYSIS_FILE=$(find "$OUTPUT_DIR" -name "*.analyse.md" -type f 2>/dev/null | head -1) + + # Build result JSON + { + echo '{' + echo '"success": true,' + echo '"job_id": "'"$JOB_ID"'",' + + # Metadata + if [ -f "$META_FILE" ]; then + echo '"metadata": ' + cat "$META_FILE" + echo ',' + else + echo '"metadata": null,' + fi + + # Transcript + if [ -f "$TRANSCRIPT_FILE" ]; then + printf '"transcript": ' + if command -v jq >/dev/null 2>&1; then + cat "$TRANSCRIPT_FILE" | jq -Rs '.' + else + printf '"%s"' "$(cat "$TRANSCRIPT_FILE" | sed 's/\\/\\\\/g; s/"/\\"/g; s/$/\\n/' | tr -d '\n')" + fi + echo ',' + else + echo '"transcript": null,' + fi + + # Analysis + if [ -f "$ANALYSIS_FILE" ]; then + printf '"analysis": ' + if command -v jq >/dev/null 2>&1; then + cat "$ANALYSIS_FILE" | jq -Rs '.' + else + printf '"%s"' "$(cat "$ANALYSIS_FILE" | sed 's/\\/\\\\/g; s/"/\\"/g; s/$/\\n/' | tr -d '\n')" + fi + else + echo '"analysis": null' + fi + + echo '}' + } > "$RESULT_FILE" + else + echo "failed" > "$STATUS_FILE" + echo '{"success": false, "error": "Analysis failed", "job_id": "'"$JOB_ID"'"}' > "$RESULT_FILE" + fi +) & + +# Return job ID for polling +echo "{\"success\": true, \"message\": \"Analysis started\", \"job_id\": \"$JOB_ID\"}" diff --git a/package/secubox/secubox-app-peertube/files/www/cgi-bin/peertube-analyse-status b/package/secubox/secubox-app-peertube/files/www/cgi-bin/peertube-analyse-status new file mode 100644 index 00000000..71a62386 --- /dev/null +++ b/package/secubox/secubox-app-peertube/files/www/cgi-bin/peertube-analyse-status @@ -0,0 +1,64 @@ +#!/bin/sh +# CGI endpoint for checking PeerTube analysis status +# Returns JSON response + +# Set headers +printf "Content-Type: application/json\r\n" +printf "Access-Control-Allow-Origin: *\r\n" +printf "\r\n" + +# Get job_id from query string or POST body +JOB_ID="" + +if [ -n "$QUERY_STRING" ]; then + JOB_ID=$(echo "$QUERY_STRING" | sed -n 's/.*job_id=\([^&]*\).*/\1/p') +fi + +if [ -z "$JOB_ID" ] && [ "$REQUEST_METHOD" = "POST" ]; then + INPUT=$(cat) + if command -v jq >/dev/null 2>&1; then + JOB_ID=$(echo "$INPUT" | jq -r '.job_id // empty') + else + JOB_ID=$(echo "$INPUT" | jsonfilter -e '@.job_id' 2>/dev/null) + fi +fi + +if [ -z "$JOB_ID" ]; then + echo '{"error": "job_id is required"}' + exit 0 +fi + +# Sanitize job_id (only allow alphanumeric and underscore) +JOB_ID=$(echo "$JOB_ID" | tr -cd 'a-zA-Z0-9_') + +STATUS_FILE="/tmp/peertube-analyse-${JOB_ID}.status" +RESULT_FILE="/tmp/peertube-analyse-${JOB_ID}.json" +LOG_FILE="/tmp/peertube-analyse-${JOB_ID}.log" + +# Check if job exists +if [ ! -f "$STATUS_FILE" ]; then + echo '{"error": "Job not found", "job_id": "'"$JOB_ID"'"}' + exit 0 +fi + +STATUS=$(cat "$STATUS_FILE" 2>/dev/null || echo "unknown") + +# If completed, return full result +if [ "$STATUS" = "completed" ] && [ -f "$RESULT_FILE" ]; then + cat "$RESULT_FILE" + exit 0 +fi + +# Otherwise return status with logs +LOGS="" +if [ -f "$LOG_FILE" ]; then + LOGS=$(tail -5 "$LOG_FILE" 2>/dev/null | tr '\n' ' ' | sed 's/"/\\"/g') +fi + +cat << EOF +{ + "status": "$STATUS", + "job_id": "$JOB_ID", + "logs": "$LOGS" +} +EOF diff --git a/package/secubox/secubox-app-peertube/files/www/peertube-analyse/index.html b/package/secubox/secubox-app-peertube/files/www/peertube-analyse/index.html new file mode 100644 index 00000000..f87e3e31 --- /dev/null +++ b/package/secubox/secubox-app-peertube/files/www/peertube-analyse/index.html @@ -0,0 +1,762 @@ + + + + + +PeerTube Analyse — SecuBox Intelligence + + + + + + + + + +