231 lines
7.7 KiB
Bash
231 lines
7.7 KiB
Bash
#!/bin/bash
|
|
# ==============================================================================================
|
|
# ================================= DECISION ENGINE ============================================
|
|
# ==============================================================================================
|
|
# Central behavior-driven decision kernel used by media automation systems.
|
|
#
|
|
# This engine does NOT download media.
|
|
# This engine does NOT search indexers.
|
|
# This engine does NOT manage applications directly.
|
|
#
|
|
# Instead:
|
|
# It evaluates candidates.
|
|
# Scores them against ecosystem behavior.
|
|
# Applies adaptive filtering rules.
|
|
# Returns decisions to consumer scripts.
|
|
#
|
|
# ==============================================================================================
|
|
# ── DESIGN PHILOSOPHY ─────────────────────────────────────────────────────────────────────────
|
|
#
|
|
# The ecosystem is built around:
|
|
#
|
|
# Family-aware decisions
|
|
# Time-aware weighting
|
|
# Behavior-driven adaptation
|
|
# Domain-specific strictness
|
|
#
|
|
# Each media domain consumes the engine differently:
|
|
#
|
|
# Lidarr → highly selective, quality-first discovery
|
|
# Sonarr → balanced family-aware episodic intake
|
|
# Radarr → broader flexibility with intelligent filtering
|
|
#
|
|
# The engine itself remains domain-agnostic.
|
|
# Consumers define their own thresholds, weights, and strictness profiles.
|
|
#
|
|
# This separation prevents:
|
|
#
|
|
# Cross-domain bias pollution
|
|
# Unified-feed degeneration
|
|
# Overfitting to a single user's habits
|
|
# Low-quality recommendation drift over time
|
|
#
|
|
# Result:
|
|
#
|
|
# Music stays curated and intentional
|
|
# TV stays balanced across users
|
|
# Movies remain adaptive without chaos
|
|
#
|
|
# ==============================================================================================
|
|
# ── RESPONSIBILITIES ──────────────────────────────────────────────────────────────────────────
|
|
#
|
|
# The decision engine is responsible for:
|
|
#
|
|
# Candidate scoring
|
|
# User weighting
|
|
# Temporal decay
|
|
# Popularity normalization
|
|
# Duplicate prevention
|
|
# Strictness enforcement
|
|
# Threshold evaluation
|
|
# Final decision output
|
|
#
|
|
# The engine returns:
|
|
#
|
|
# ACCEPT
|
|
# REJECT
|
|
# SCORE
|
|
# REASON
|
|
#
|
|
# Consumer scripts decide what to do with the result.
|
|
#
|
|
# ==============================================================================================
|
|
# ── ECOSYSTEM ROLE ────────────────────────────────────────────────────────────────────────────
|
|
#
|
|
# Kernel Position:
|
|
#
|
|
# Kernel/
|
|
# ├── decision_engine.sh
|
|
# ├── transcoding_engine.sh
|
|
# ├── future_engine_modules...
|
|
#
|
|
# Shared reusable logic belongs in:
|
|
#
|
|
# common.sh
|
|
#
|
|
# Shared ecosystem configuration belongs in:
|
|
#
|
|
# master.conf
|
|
#
|
|
# Host-specific secrets/configuration belong in:
|
|
#
|
|
# master_host*.conf
|
|
#
|
|
# The kernel contains:
|
|
#
|
|
# Stateful logic
|
|
# Adaptive systems
|
|
# Scoring systems
|
|
# Cross-domain intelligence
|
|
#
|
|
# ==============================================================================================
|
|
# ── VERSION ───────────────────────────────────────────────────────────────────────────────────
|
|
#
|
|
# v1.0
|
|
# Initial decision kernel architecture
|
|
# Built first for Lidarr discovery orchestration
|
|
#
|
|
# ==============================================================================================
|
|
|
|
# ==============================================================================================
|
|
# ── SCORE CANDIDATE ───────────────────────────────────────────────────────────────────────────
|
|
# ==============================================================================================
|
|
# Calculates weighted score for a media candidate.
|
|
#
|
|
# Inputs:
|
|
# USER_SCORE
|
|
# POPULARITY_SCORE
|
|
# RECENCY_SCORE
|
|
# QUALITY_SCORE
|
|
#
|
|
# Output:
|
|
# TOTAL_SCORE
|
|
#
|
|
# Consumer scripts define actual weighting values.
|
|
|
|
score_candidate() {
|
|
|
|
local user_score="${1:-0}"
|
|
local popularity_score="${2:-0}"
|
|
local recency_score="${3:-0}"
|
|
local quality_score="${4:-0}"
|
|
|
|
TOTAL_SCORE=$(( \
|
|
user_score + \
|
|
popularity_score + \
|
|
recency_score + \
|
|
quality_score \
|
|
))
|
|
|
|
echo "$TOTAL_SCORE"
|
|
}
|
|
|
|
# ==============================================================================================
|
|
# ── THRESHOLD CHECK ───────────────────────────────────────────────────────────────────────────
|
|
# ==============================================================================================
|
|
# Determines if candidate passes scoring threshold.
|
|
#
|
|
# Usage:
|
|
# evaluate_threshold "$score" "$minimum"
|
|
|
|
evaluate_threshold() {
|
|
|
|
local score="$1"
|
|
local minimum="$2"
|
|
|
|
if (( score >= minimum )); then
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# ==============================================================================================
|
|
# ── TEMPORAL DECAY ────────────────────────────────────────────────────────────────────────────
|
|
# ==============================================================================================
|
|
# Reduces influence of old behavior over time.
|
|
#
|
|
# Prevents:
|
|
# Permanent genre lock-in
|
|
# Historical bias accumulation
|
|
# Dead-user dominance
|
|
#
|
|
# Usage:
|
|
# apply_temporal_decay current_score age_days
|
|
|
|
apply_temporal_decay() {
|
|
|
|
local score="$1"
|
|
local age_days="$2"
|
|
|
|
local decay=$(( age_days / 30 ))
|
|
|
|
local adjusted=$(( score - decay ))
|
|
|
|
(( adjusted < 0 )) && adjusted=0
|
|
|
|
echo "$adjusted"
|
|
}
|
|
|
|
# ==============================================================================================
|
|
# ── DUPLICATE PROTECTION ──────────────────────────────────────────────────────────────────────
|
|
# ==============================================================================================
|
|
# Prevents repetitive acquisitions.
|
|
#
|
|
# Consumer defines:
|
|
# cooldown periods
|
|
# replay windows
|
|
# duplicate tolerance
|
|
#
|
|
# Returns:
|
|
# 0 = duplicate
|
|
# 1 = unique
|
|
|
|
is_duplicate_candidate() {
|
|
|
|
local candidate="$1"
|
|
local history_file="$2"
|
|
|
|
grep -qi "^${candidate}$" "$history_file" 2>/dev/null
|
|
}
|
|
|
|
# ==============================================================================================
|
|
# ── FINAL DECISION ────────────────────────────────────────────────────────────────────────────
|
|
# ==============================================================================================
|
|
# Produces final engine verdict.
|
|
#
|
|
# Outputs:
|
|
# ACCEPT
|
|
# REJECT
|
|
|
|
make_decision() {
|
|
|
|
local score="$1"
|
|
local threshold="$2"
|
|
|
|
if evaluate_threshold "$score" "$threshold"; then
|
|
echo "ACCEPT"
|
|
else
|
|
echo "REJECT"
|
|
fi
|
|
} |