feat: slskd reconnect guard in downloaders_reset, mass v2 sync
- downloaders_reset: connection check block before slskd API sections; triggers PUT /api/v0/server reconnect if disconnected, polls 60s, gates Stuck Searches and Dead Transfer Records on SLSKD_CONNECTED - Sync all modified/new/deleted files from v2 refactor across Docker_Essentials, Media, Monitors, Partnership, Rsync, Tools, Transcodes, unRAID_Essentials, common.sh, master confs, and new Manual/README docs
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
# ━━━━━ KERNEL ━━━━━
|
||||
|
||||
Shared intelligence layer for media automation. Sourced by consumer scripts —
|
||||
not run directly. Contains stateful scoring systems, adaptive logic, and
|
||||
cross-domain engines that would pollute common.sh if placed there.
|
||||
|
||||
---
|
||||
|
||||
## ━━━ WHAT THIS FOLDER IS ━━━
|
||||
|
||||
**Not scripts. Not utilities. Engines.**
|
||||
|
||||
The Kernel holds logic that is:
|
||||
|
||||
- **Stateful** — maintains scores, decay state, or adaptive thresholds across calls
|
||||
- **Domain-aware** — understands the difference between music, TV, and movie decisions
|
||||
- **Shared across consumers** — one engine, multiple discovery scripts consuming it
|
||||
|
||||
This is distinct from:
|
||||
|
||||
| Location | Contains |
|
||||
|----------|----------|
|
||||
| `common.sh` | Reusable utility functions (logging, locking, arg parsing) |
|
||||
| `master.conf` | Shared ecosystem configuration and tuning values |
|
||||
| `Kernel/` | Stateful scoring systems and adaptive decision logic |
|
||||
|
||||
The rule: if the logic makes *decisions*, it lives here. If it's a helper or
|
||||
config value, it lives in common.sh or master.conf.
|
||||
|
||||
---
|
||||
|
||||
## ━━━ WHY A SEPARATE KERNEL ━━━
|
||||
|
||||
Media automation decisions are not uniform. A Lidarr discovery script should be
|
||||
highly selective. A Sonarr intake script should be family-aware and balanced. A
|
||||
Radarr script should be broadly flexible. If scoring logic lives in each script,
|
||||
it diverges and drifts independently.
|
||||
|
||||
Centralizing the decision layer:
|
||||
|
||||
- Keeps consumers thin — they define weights and thresholds, not scoring logic
|
||||
- Prevents cross-domain bias pollution — music strictness does not contaminate TV
|
||||
- Allows the scoring model to improve without touching every consumer script
|
||||
|
||||
---
|
||||
|
||||
## ━━━ WHAT BELONGS HERE ━━━
|
||||
|
||||
An engine belongs in Kernel if it:
|
||||
|
||||
- Implements a scoring or evaluation model
|
||||
- Maintains or reads adaptive state
|
||||
- Is consumed by more than one script (or is designed to be)
|
||||
- Contains logic that would create tight coupling if duplicated
|
||||
|
||||
Utilities — locking, notifications, arg parsing, API calls — belong in common.sh.
|
||||
|
||||
---
|
||||
|
||||
## ━━━ FILES IN THIS FOLDER ━━━
|
||||
|
||||
| File | Role | Status |
|
||||
|------|------|--------|
|
||||
| `decision_engine.sh` | Behavior-driven scoring kernel — scoring, decay, deduplication, threshold evaluation | WIP — currently paired with `playback_aware_lidarr_discovery.sh` |
|
||||
|
||||
**Planned:**
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `transcoding_engine.sh` | Transcoding decision logic (quality, codec selection, cost-benefit evaluation) |
|
||||
|
||||
---
|
||||
|
||||
## ━━━ HOW CONSUMERS USE THE KERNEL ━━━
|
||||
|
||||
Source the engine at the top of the consumer script:
|
||||
|
||||
```bash
|
||||
source "$ROOT_DIR/Kernel/decision_engine.sh"
|
||||
```
|
||||
|
||||
Then call engine functions directly, passing consumer-defined weights and thresholds:
|
||||
|
||||
```bash
|
||||
score=$(score_candidate "$user_score" "$popularity" "$recency" "$quality")
|
||||
score=$(apply_temporal_decay "$score" "$age_days")
|
||||
decision=$(make_decision "$score" "$MY_THRESHOLD")
|
||||
```
|
||||
|
||||
The engine returns values — consumer scripts decide what to do with them. The
|
||||
engine has no side effects: no file writes, no API calls, no deletions.
|
||||
+43
-147
@@ -1,128 +1,64 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================================
|
||||
# ================================= DECISION ENGINE ============================================
|
||||
# ================================= 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.
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Behavior-driven scoring kernel for media automation. Sourced by consumer
|
||||
# scripts — not run directly. Evaluates candidates, applies weighted scoring,
|
||||
# temporal decay, and deduplication, then returns a verdict.
|
||||
#
|
||||
# Instead:
|
||||
# It evaluates candidates.
|
||||
# Scores them against ecosystem behavior.
|
||||
# Applies adaptive filtering rules.
|
||||
# Returns decisions to consumer scripts.
|
||||
# Does NOT download media, search indexers, or manage applications. Has no
|
||||
# side effects — no file writes, no API calls, no deletions.
|
||||
#
|
||||
# Currently paired with: Media/playback_aware_lidarr_discovery.sh
|
||||
#
|
||||
# ==============================================================================================
|
||||
# ── DESIGN PHILOSOPHY ─────────────────────────────────────────────────────────────────────────
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# The ecosystem is built around:
|
||||
# Domain-Agnostic Core
|
||||
# The engine itself has no knowledge of music vs TV vs movies. Consumers
|
||||
# define thresholds, weights, and strictness profiles — the engine just
|
||||
# scores and decides. This prevents cross-domain bias pollution: music
|
||||
# strictness cannot contaminate TV intake logic.
|
||||
#
|
||||
# Family-aware decisions
|
||||
# Time-aware weighting
|
||||
# Behavior-driven adaptation
|
||||
# Domain-specific strictness
|
||||
# Lidarr consumers → highly selective, quality-first discovery
|
||||
# Sonarr consumers → balanced, family-aware episodic intake
|
||||
# Radarr consumers → broader flexibility with intelligent filtering
|
||||
#
|
||||
# Each media domain consumes the engine differently:
|
||||
# Temporal Decay
|
||||
# Old behavioral signals lose influence over time (one decay unit per 30
|
||||
# days). Prevents permanent genre lock-in, historical bias accumulation,
|
||||
# and dead-user score dominance.
|
||||
#
|
||||
# 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
|
||||
# Consumer Owns the Decision
|
||||
# The engine returns ACCEPT/REJECT/SCORE. What happens next is entirely
|
||||
# the consumer's concern — the engine never acts on its own verdict.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# ── 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.
|
||||
#
|
||||
# FUNCTIONS
|
||||
# ==============================================================================================
|
||||
# ── ECOSYSTEM ROLE ────────────────────────────────────────────────────────────────────────────
|
||||
#
|
||||
# Kernel Position:
|
||||
# score_candidate user_score popularity_score recency_score quality_score
|
||||
# Returns TOTAL_SCORE (integer sum). Consumer defines actual weight values.
|
||||
#
|
||||
# Kernel/
|
||||
# ├── decision_engine.sh
|
||||
# ├── transcoding_engine.sh
|
||||
# ├── future_engine_modules...
|
||||
# evaluate_threshold score minimum
|
||||
# Returns 0 (pass) or 1 (fail). Used as: if evaluate_threshold ...
|
||||
#
|
||||
# Shared reusable logic belongs in:
|
||||
# apply_temporal_decay score age_days
|
||||
# Returns adjusted score. Subtracts (age_days / 30), floor at 0.
|
||||
#
|
||||
# common.sh
|
||||
# is_duplicate_candidate candidate history_file
|
||||
# Returns 0 (duplicate found in history_file) or 1 (not found).
|
||||
#
|
||||
# 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
|
||||
# make_decision score threshold
|
||||
# Returns "ACCEPT" or "REJECT". Calls evaluate_threshold internally.
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
# ==============================================================================================
|
||||
# ── 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 ───────────────────────────────────────────────────────────
|
||||
score_candidate() {
|
||||
|
||||
local user_score="${1:-0}"
|
||||
@@ -140,14 +76,7 @@ score_candidate() {
|
||||
echo "$TOTAL_SCORE"
|
||||
}
|
||||
|
||||
# ==============================================================================================
|
||||
# ── THRESHOLD CHECK ───────────────────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
# Determines if candidate passes scoring threshold.
|
||||
#
|
||||
# Usage:
|
||||
# evaluate_threshold "$score" "$minimum"
|
||||
|
||||
# ── evaluate_threshold ───────────────────────────────────────────────────────
|
||||
evaluate_threshold() {
|
||||
|
||||
local score="$1"
|
||||
@@ -160,19 +89,7 @@ evaluate_threshold() {
|
||||
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 ─────────────────────────────────────────────────────
|
||||
apply_temporal_decay() {
|
||||
|
||||
local score="$1"
|
||||
@@ -187,20 +104,7 @@ apply_temporal_decay() {
|
||||
echo "$adjusted"
|
||||
}
|
||||
|
||||
# ==============================================================================================
|
||||
# ── DUPLICATE PROTECTION ──────────────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
# Prevents repetitive acquisitions.
|
||||
#
|
||||
# Consumer defines:
|
||||
# cooldown periods
|
||||
# replay windows
|
||||
# duplicate tolerance
|
||||
#
|
||||
# Returns:
|
||||
# 0 = duplicate
|
||||
# 1 = unique
|
||||
|
||||
# ── is_duplicate_candidate ───────────────────────────────────────────────────
|
||||
is_duplicate_candidate() {
|
||||
|
||||
local candidate="$1"
|
||||
@@ -209,15 +113,7 @@ is_duplicate_candidate() {
|
||||
grep -qi "^${candidate}$" "$history_file" 2>/dev/null
|
||||
}
|
||||
|
||||
# ==============================================================================================
|
||||
# ── FINAL DECISION ────────────────────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
# Produces final engine verdict.
|
||||
#
|
||||
# Outputs:
|
||||
# ACCEPT
|
||||
# REJECT
|
||||
|
||||
# ── make_decision ─────────────────────────────────────────────────────────────
|
||||
make_decision() {
|
||||
|
||||
local score="$1"
|
||||
|
||||
Reference in New Issue
Block a user