fix(hub): Fix NFO extraction for BusyBox awk compatibility

- Replace gsub(/[\[\]]/) with two sub() calls for section parsing
- Use explicit pattern matching for each NFO field
- Single-pass awk extraction for all 7 fields (category, desc, keywords, caps, audience, icon, version)
- Remove NFO parser library dependency (now uses direct awk)
- Simplify capability tracking with tr instead of for loop

Tested: 110 NFO entries now correctly extracted from 239 total items

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-03-14 10:10:01 +01:00
parent 3f78308d2c
commit 9deddca53a

View File

@ -10,11 +10,7 @@ PEERTUBE_URL="https://tube.gk2.secubox.in"
STREAMLIT_APPS_DIR="/srv/streamlit/apps" STREAMLIT_APPS_DIR="/srv/streamlit/apps"
METABLOG_SITES_DIR="/srv/metablogizer/sites" METABLOG_SITES_DIR="/srv/metablogizer/sites"
# Load NFO parser if available # Fast NFO field extraction using awk (no eval, no subshell parsing)
NFO_PARSER="/usr/share/streamlit-forge/lib/nfo-parser.sh"
[ -f "$NFO_PARSER" ] && . "$NFO_PARSER"
# Get app info from NFO file
# Usage: get_nfo_info <app_dir> <section> <field> <default> # Usage: get_nfo_info <app_dir> <section> <field> <default>
get_nfo_info() { get_nfo_info() {
local app_dir="$1" local app_dir="$1"
@ -25,48 +21,59 @@ get_nfo_info() {
local nfo_file="$app_dir/README.nfo" local nfo_file="$app_dir/README.nfo"
[ ! -f "$nfo_file" ] && { echo "$default"; return; } [ ! -f "$nfo_file" ] && { echo "$default"; return; }
# Use nfo_get from parser if available # Direct awk extraction - fast and reliable
if type nfo_get >/dev/null 2>&1; then local val=$(awk -v section="$section" -v field="$field" '
local val=$(nfo_get "$nfo_file" "$section" "$field" 2>/dev/null) BEGIN { in_section = 0 }
[ -n "$val" ] && echo "$val" || echo "$default" /^\[/ {
return if ($0 ~ "\\[" section "\\]") { in_section = 1 }
fi else { in_section = 0 }
}
# Fallback: section-aware grep parsing in_section && /^[a-zA-Z_-]+=/ {
awk -v section="$section" -v field="$field" ' # Extract key and value
/^\[/ { in_section = ($0 ~ "\\[" section "\\]") } key = $0
in_section && /^[a-zA-Z_]+=/ { sub(/=.*/, "", key)
split($0, kv, "=") gsub(/^[ \t]+|[ \t]+$/, "", key)
gsub(/^[ \t]+|[ \t]+$/, "", kv[1]) gsub(/-/, "_", key)
if (kv[1] == field) { if (key == field) {
gsub(/^[^=]+=/, "") val = $0
gsub(/^[ \t]+|[ \t]+$/, "") sub(/^[^=]+=[ \t]*/, "", val)
print gsub(/^["'"'"']|["'"'"']$/, "", val)
print val
exit exit
} }
} }
' "$nfo_file" 2>/dev/null | head -1 ' "$nfo_file" 2>/dev/null)
# Return default if nothing found [ -n "$val" ] && echo "$val" || echo "$default"
[ -z "$(cat /dev/stdin)" ] && echo "$default"
} }
# Get all NFO metadata for an app # Get all NFO metadata for an app in single awk pass
# Returns: category|description|keywords|capabilities|audience # Returns: category|description|keywords|capabilities|audience|icon|version
get_nfo_full() { get_nfo_full() {
local app_dir="$1" local app_dir="$1"
local nfo_file="$app_dir/README.nfo" local nfo_file="$app_dir/README.nfo"
[ ! -f "$nfo_file" ] && return 1 [ ! -f "$nfo_file" ] && return 1
local category=$(get_nfo_info "$app_dir" "tags" "category" "") # Single-pass extraction of all needed fields
local desc=$(get_nfo_info "$app_dir" "description" "short" "") awk '
local keywords=$(get_nfo_info "$app_dir" "tags" "keywords" "") BEGIN { section = "" }
local capabilities=$(get_nfo_info "$app_dir" "dynamics" "capabilities" "") /^\[/ {
local audience=$(get_nfo_info "$app_dir" "tags" "audience" "") section = $0
local icon=$(get_nfo_info "$app_dir" "media" "icon" "") sub(/^\[/, "", section)
sub(/\]$/, "", section)
echo "${category}|${desc}|${keywords}|${capabilities}|${audience}|${icon}" }
section == "tags" && /^category=/ { val=$0; sub(/^category=/, "", val); category=val }
section == "tags" && /^keywords=/ { val=$0; sub(/^keywords=/, "", val); keywords=val }
section == "tags" && /^audience=/ { val=$0; sub(/^audience=/, "", val); audience=val }
section == "description" && /^short=/ { val=$0; sub(/^short=/, "", val); desc=val }
section == "dynamics" && /^capabilities=/ { val=$0; sub(/^capabilities=/, "", val); caps=val }
section == "media" && /^icon=/ { val=$0; sub(/^icon=/, "", val); icon=val }
section == "identity" && /^version=/ { val=$0; sub(/^version=/, "", val); version=val }
END {
printf "%s|%s|%s|%s|%s|%s|%s\n", category, desc, keywords, caps, audience, icon, version
}
' "$nfo_file" 2>/dev/null
} }
categorize_site() { categorize_site() {
@ -290,7 +297,7 @@ uci show metablogizer 2>/dev/null | grep "=site$" | sed "s/metablogizer\.\(.*\)=
nfo_keywords=$(echo "$nfo_data" | cut -d'|' -f3) nfo_keywords=$(echo "$nfo_data" | cut -d'|' -f3)
nfo_caps=$(echo "$nfo_data" | cut -d'|' -f4) nfo_caps=$(echo "$nfo_data" | cut -d'|' -f4)
nfo_audience=$(echo "$nfo_data" | cut -d'|' -f5) nfo_audience=$(echo "$nfo_data" | cut -d'|' -f5)
nfo_version=$(get_nfo_info "$site_dir" "identity" "version" "") nfo_version=$(echo "$nfo_data" | cut -d'|' -f7)
else else
nfo_cat="" nfo_cat=""
nfo_desc="" nfo_desc=""
@ -301,23 +308,13 @@ uci show metablogizer 2>/dev/null | grep "=site$" | sed "s/metablogizer\.\(.*\)=
fi fi
# Use NFO category or fallback to name-based categorization # Use NFO category or fallback to name-based categorization
if [ -n "$nfo_cat" ]; then [ -n "$nfo_cat" ] && cat="$nfo_cat" || cat=$(categorize_site "$name")
cat="$nfo_cat"
else
cat=$(categorize_site "$name")
fi
emoji=$(get_emoji "$cat") emoji=$(get_emoji "$cat")
echo "$cat" >> "$CAT_FILE" echo "$cat" >> "$CAT_FILE"
# Track audiences for filter tabs # Track audiences and capabilities for filters
[ -n "$nfo_audience" ] && echo "$nfo_audience" >> "/tmp/hub_audiences_$$.txt" [ -n "$nfo_audience" ] && echo "$nfo_audience" >> "/tmp/hub_audiences_$$.txt"
[ -n "$nfo_caps" ] && echo "$nfo_caps" | tr ',' '\n' >> "/tmp/hub_caps_$$.txt"
# Track capabilities for filter
if [ -n "$nfo_caps" ]; then
for cap in $(echo "$nfo_caps" | tr ',' ' '); do
[ -n "$cap" ] && echo "$cap" >> "/tmp/hub_caps_$$.txt"
done
fi
protected="-" protected="-"
[ "$auth_required" = "1" ] && protected="protected" [ "$auth_required" = "1" ] && protected="protected"
@ -346,7 +343,7 @@ uci show streamlit 2>/dev/null | grep "=instance$" | sed "s/streamlit\.\(.*\)=in
nfo_keywords=$(echo "$nfo_data" | cut -d'|' -f3) nfo_keywords=$(echo "$nfo_data" | cut -d'|' -f3)
nfo_caps=$(echo "$nfo_data" | cut -d'|' -f4) nfo_caps=$(echo "$nfo_data" | cut -d'|' -f4)
nfo_audience=$(echo "$nfo_data" | cut -d'|' -f5) nfo_audience=$(echo "$nfo_data" | cut -d'|' -f5)
nfo_version=$(get_nfo_info "$app_dir" "identity" "version" "") nfo_version=$(echo "$nfo_data" | cut -d'|' -f7)
else else
nfo_cat="" nfo_cat=""
nfo_desc="" nfo_desc=""
@ -356,23 +353,13 @@ uci show streamlit 2>/dev/null | grep "=instance$" | sed "s/streamlit\.\(.*\)=in
nfo_version="" nfo_version=""
fi fi
if [ -n "$nfo_cat" ]; then [ -n "$nfo_cat" ] && cat="$nfo_cat" || cat=$(categorize_site "$name")
cat="$nfo_cat"
else
cat=$(categorize_site "$name")
fi
emoji=$(get_emoji "$cat") emoji=$(get_emoji "$cat")
echo "$cat" >> "$CAT_FILE" echo "$cat" >> "$CAT_FILE"
# Track audiences for filter tabs # Track audiences and capabilities for filters
[ -n "$nfo_audience" ] && echo "$nfo_audience" >> "/tmp/hub_audiences_$$.txt" [ -n "$nfo_audience" ] && echo "$nfo_audience" >> "/tmp/hub_audiences_$$.txt"
[ -n "$nfo_caps" ] && echo "$nfo_caps" | tr ',' '\n' >> "/tmp/hub_caps_$$.txt"
# Track capabilities for filter
if [ -n "$nfo_caps" ]; then
for cap in $(echo "$nfo_caps" | tr ',' ' '); do
[ -n "$cap" ] && echo "$cap" >> "/tmp/hub_caps_$$.txt"
done
fi
# Format: domain name cat emoji type thumb protected desc keywords caps version audience # Format: domain name cat emoji type thumb protected desc keywords caps version audience
printf '%s\t%s\t%s\t%s\tstreamlit\t-\t-\t%s\t%s\t%s\t%s\t%s\n' \ printf '%s\t%s\t%s\t%s\tstreamlit\t-\t-\t%s\t%s\t%s\t%s\t%s\n' \
@ -401,7 +388,7 @@ uci show streamlit-forge 2>/dev/null | grep "=app$" | sed "s/streamlit-forge\.\(
nfo_keywords=$(echo "$nfo_data" | cut -d'|' -f3) nfo_keywords=$(echo "$nfo_data" | cut -d'|' -f3)
nfo_caps=$(echo "$nfo_data" | cut -d'|' -f4) nfo_caps=$(echo "$nfo_data" | cut -d'|' -f4)
nfo_audience=$(echo "$nfo_data" | cut -d'|' -f5) nfo_audience=$(echo "$nfo_data" | cut -d'|' -f5)
nfo_version=$(get_nfo_info "$app_dir" "identity" "version" "") nfo_version=$(echo "$nfo_data" | cut -d'|' -f7)
else else
nfo_cat="" nfo_cat=""
nfo_desc="" nfo_desc=""
@ -411,21 +398,13 @@ uci show streamlit-forge 2>/dev/null | grep "=app$" | sed "s/streamlit-forge\.\(
nfo_version="" nfo_version=""
fi fi
if [ -n "$nfo_cat" ]; then [ -n "$nfo_cat" ] && cat="$nfo_cat" || cat=$(categorize_site "$name")
cat="$nfo_cat"
else
cat=$(categorize_site "$name")
fi
emoji=$(get_emoji "$cat") emoji=$(get_emoji "$cat")
echo "$cat" >> "$CAT_FILE" echo "$cat" >> "$CAT_FILE"
# Track audiences and capabilities # Track audiences and capabilities
[ -n "$nfo_audience" ] && echo "$nfo_audience" >> "/tmp/hub_audiences_$$.txt" [ -n "$nfo_audience" ] && echo "$nfo_audience" >> "/tmp/hub_audiences_$$.txt"
if [ -n "$nfo_caps" ]; then [ -n "$nfo_caps" ] && echo "$nfo_caps" | tr ',' '\n' >> "/tmp/hub_caps_$$.txt"
for cap in $(echo "$nfo_caps" | tr ',' ' '); do
[ -n "$cap" ] && echo "$cap" >> "/tmp/hub_caps_$$.txt"
done
fi
printf '%s\t%s\t%s\t%s\tstreamlit\t-\t-\t%s\t%s\t%s\t%s\t%s\n' \ printf '%s\t%s\t%s\t%s\tstreamlit\t-\t-\t%s\t%s\t%s\t%s\t%s\n' \
"$domain" "$name" "$cat" "$emoji" \ "$domain" "$name" "$cat" "$emoji" \