massive update. Master conf split, now modular with a load sceriprt to drive all configs to scripts. with unraid scpecific safeguard tests , and improved standardized ux. including dynamic host detect, who am i who else it there. EVERY SINGLE SCRIPT UPDATED. DEBATING THAT THIS IS ACUALLY V2
This commit is contained in:
+214
-78
@@ -1,37 +1,75 @@
|
||||
#!/bin/bash
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# --------------------------------- Emby Session Report ----------------------------------------
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# Generates a weekly usage report from the Emby media server via its API.
|
||||
# Queries activity logs, session history and library stats to produce a
|
||||
# human-readable summary of what was watched, by whom and how.
|
||||
# ==============================================================================================
|
||||
# ================================= Emby Session Report ========================================
|
||||
# ==============================================================================================
|
||||
# Generates a usage report from the Emby media server via its API.
|
||||
# Queries activity logs and session history to produce a summary of what was
|
||||
# watched, by whom, and how over the configured report period.
|
||||
#
|
||||
# Report includes:
|
||||
# Total streams during the report period
|
||||
# Transcode vs direct play ratio
|
||||
# Live TV usage
|
||||
# Top N most watched content
|
||||
# Most active users
|
||||
# Peak concurrent streams
|
||||
# ── REPORT INCLUDES ───────────────────────────────────────────────────────────────────────────
|
||||
# Server info — name, version, uptime
|
||||
# Active sessions — current streams, direct play vs transcode
|
||||
# Library stats — movie, episode, song counts
|
||||
# Activity history — play events from the last EMBY_REPORT_DAYS days
|
||||
# Top content — most played items in the period (top EMBY_REPORT_TOP_N)
|
||||
# Most active users — who watched the most in the period
|
||||
# Transcode ratio — how often transcoding was needed vs direct play
|
||||
# Ramdisk status — current transcode location and usage
|
||||
#
|
||||
# ── HOST AWARENESS ────────────────────────────────────────────────────────────────────────────
|
||||
# detect_hosts() sets MY_ID and aliases EMBY_URL and EMBY_API_KEY.
|
||||
# Each server reports on its own Emby instance automatically.
|
||||
#
|
||||
# ── BEHAVIOUR ─────────────────────────────────────────────────────────────────────────────────
|
||||
# No persistent writes — queries API fresh each run.
|
||||
# All configuration in Master.conf under Emby Session Report section.
|
||||
# Supports --dry-run to test API connectivity without sending notification.
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# This is a monitor/report script — SILENT_MODE=false — output is the point.
|
||||
# Silent when healthy (no notification on clean run).
|
||||
# Notifies only if transcoding is very high (>80% of streams) — may indicate config issue.
|
||||
#
|
||||
# ── SAFEGUARDS ────────────────────────────────────────────────────────────────────────────────
|
||||
# acquire_lock — prevents duplicate reports running simultaneously
|
||||
# check_api() — verifies Emby reachable before queries
|
||||
# jq + curl validation — exits if either tool missing
|
||||
# validate_unraid_cmd — notify script validated before use
|
||||
# Per-section guards — API failure in one section does not abort others
|
||||
#
|
||||
# ── CONFIGURATION (master_host*.conf) ─────────────────────────────────────────────────────────
|
||||
# HOST*_EMBY_URL / HOST*_EMBY_API_KEY
|
||||
# Aliased by detect_hosts() — script uses EMBY_URL / EMBY_API_KEY
|
||||
#
|
||||
# ── CONFIGURATION (master.conf) ───────────────────────────────────────────────────────────────
|
||||
# EMBY_REPORT_DAYS — days to include in the report period (default 7)
|
||||
# EMBY_REPORT_TOP_N — number of top content items to show (default 10)
|
||||
# RAMDISK_PATH — ramdisk mount path (for transcode status)
|
||||
# TRANSCODE_LINK — symlink path (for transcode location)
|
||||
#
|
||||
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
|
||||
# emby_session_report.sh — generate report
|
||||
# emby_session_report.sh --dry-run — test API connectivity only, no notification
|
||||
# emby_session_report.sh --log — verbose output
|
||||
# emby_session_report.sh --status — show config and exit
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
source "$SCRIPT_DIR/../Master.conf"
|
||||
source "$SCRIPT_DIR/../common.sh"
|
||||
source "$SCRIPT_DIR/../load_config.sh"
|
||||
|
||||
# Monitor/report script — output is the point
|
||||
SILENT_MODE=false
|
||||
|
||||
parse_args "$@"
|
||||
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# ━━━ $ICON_GEAR Setup ━━━
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# ==============================================================================================
|
||||
# ━━━ Setup ━━━
|
||||
# ==============================================================================================
|
||||
echo ""
|
||||
echo "━━━ $ICON_GEAR Setup ━━━"
|
||||
|
||||
if [[ "$EUID" -ne 0 ]]; then
|
||||
error "Must be run as root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
error "curl not found — required for Emby API calls"
|
||||
exit 1
|
||||
@@ -43,28 +81,40 @@ if ! command -v jq >/dev/null 2>&1; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Select correct Emby instance based on which server is running this script
|
||||
validate_unraid_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
# detect_hosts() sets MY_ID and aliases EMBY_URL, EMBY_API_KEY
|
||||
detect_hosts
|
||||
|
||||
if [[ "$LOCAL_SERVER_NAME" == "$HOST1" ]]; then
|
||||
EMBY_URL="$HOST1_EMBY_URL"
|
||||
EMBY_API_KEY="$HOST1_EMBY_API_KEY"
|
||||
else
|
||||
EMBY_URL="$HOST2_EMBY_URL"
|
||||
EMBY_API_KEY="$HOST2_EMBY_API_KEY"
|
||||
fi
|
||||
|
||||
info "Emby instance: $LOCAL_SERVER_NAME → $EMBY_URL"
|
||||
|
||||
require_var EMBY_URL
|
||||
require_var EMBY_API_KEY
|
||||
|
||||
success "Config validated"
|
||||
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — API will be queried but no notification sent"
|
||||
log "Emby: $EMBY_URL"
|
||||
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — API queried but no notification sent"
|
||||
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# API HELPER
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# ==============================================================================================
|
||||
# ━━━ Status ━━━
|
||||
# ==============================================================================================
|
||||
if [[ "$SHOW_STATUS" == true ]]; then
|
||||
echo ""
|
||||
echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
|
||||
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
|
||||
echo "$ICON_EMBY Emby URL: $EMBY_URL"
|
||||
echo "$ICON_TIME Period: Last ${EMBY_REPORT_DAYS} days"
|
||||
echo "$ICON_EMBY Top N: ${EMBY_REPORT_TOP_N} items"
|
||||
echo "$ICON_GEAR Dry Run: $DRY_RUN"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ==============================================================================================
|
||||
# ── API HELPER ────────────────────────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
emby_api() {
|
||||
local endpoint="$1"
|
||||
local response http_code body
|
||||
@@ -79,54 +129,66 @@ emby_api() {
|
||||
body=$(echo "$response" | head -n -1)
|
||||
|
||||
if [[ "$http_code" != "200" ]]; then
|
||||
error "Emby API returned HTTP $http_code for: $endpoint"
|
||||
error "Emby API HTTP $http_code for: $endpoint"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$body"
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# ━━━ $ICON_EMBY Emby Session Report ━━━
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# ==============================================================================================
|
||||
# ━━━ Emby Session Report ━━━
|
||||
# ==============================================================================================
|
||||
echo ""
|
||||
echo "━━━ $ICON_EMBY Emby Session Report — $(date '+%Y-%m-%d %H:%M:%S') ━━━"
|
||||
echo "$ICON_EMBY URL: $EMBY_URL"
|
||||
echo "$ICON_TIME Period: Last ${EMBY_REPORT_DAYS} days"
|
||||
echo "$ICON_HOST $MY_ID ($LOCAL_SERVER_NAME)"
|
||||
echo "$ICON_TIME Period: Last ${EMBY_REPORT_DAYS} days"
|
||||
echo ""
|
||||
|
||||
START=$(date +%s)
|
||||
|
||||
# Test connectivity
|
||||
info "Testing Emby API connectivity..."
|
||||
# ── Connectivity and server info ──────────────────────────────────────────────────────────────
|
||||
if ! check_api "$EMBY_URL" "Emby" 10; then
|
||||
notify "Emby report failed on $(hostname) — cannot connect to Emby at $EMBY_URL" \
|
||||
"Emby Report" "warning"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SYSTEM_INFO=$(emby_api "System/Info" 2>/dev/null) || {
|
||||
error "Cannot connect to Emby at $EMBY_URL"
|
||||
notify "Emby report failed on $(hostname) — cannot connect to Emby" "Emby Report" "warning"
|
||||
exit 1
|
||||
}
|
||||
|
||||
SERVER_NAME=$(echo "$SYSTEM_INFO" | jq -r '.ServerName // "Unknown"' 2>/dev/null)
|
||||
SERVER_VERSION=$(echo "$SYSTEM_INFO" | jq -r '.Version // "Unknown"' 2>/dev/null)
|
||||
success "Connected to: $SERVER_NAME (v$SERVER_VERSION)"
|
||||
echo ""
|
||||
log "Connected to: $SERVER_NAME (v$SERVER_VERSION)"
|
||||
|
||||
# Calculate date range
|
||||
REPORT_START=$(date -d "${EMBY_REPORT_DAYS} days ago" '+%Y-%m-%dT00:00:00')
|
||||
|
||||
# ── Active Sessions ──────────────────────────────────────────────────────────────────────────
|
||||
# ── Active Sessions ───────────────────────────────────────────────────────────────────────────
|
||||
echo "━━━ $ICON_EMBY Active Sessions ━━━"
|
||||
SESSIONS=$(emby_api "Sessions" 2>/dev/null) || { warn "Could not fetch sessions"; SESSIONS="[]"; }
|
||||
|
||||
ACTIVE_COUNT=$(echo "$SESSIONS" | jq '[.[] | select(.NowPlayingItem != null)] | length' 2>/dev/null || echo 0)
|
||||
TRANSCODE_COUNT=$(echo "$SESSIONS" | jq '[.[] | select(.NowPlayingItem != null) | select(.TranscodingInfo != null)] | length' 2>/dev/null || echo 0)
|
||||
DIRECT_COUNT=$(( ACTIVE_COUNT - TRANSCODE_COUNT ))
|
||||
ACTIVE_COUNT=$(echo "$SESSIONS" | \
|
||||
jq '[.[] | select(.NowPlayingItem != null)] | length' 2>/dev/null || echo 0)
|
||||
TRANSCODE_NOW=$(echo "$SESSIONS" | \
|
||||
jq '[.[] | select(.NowPlayingItem != null) | select(.TranscodingInfo != null)] | length' \
|
||||
2>/dev/null || echo 0)
|
||||
DIRECT_NOW=$(( ACTIVE_COUNT - TRANSCODE_NOW ))
|
||||
|
||||
echo " $ICON_EMBY Active streams: $ACTIVE_COUNT"
|
||||
echo " $ICON_EMBY Direct play: $DIRECT_COUNT"
|
||||
echo " $ICON_EMBY Transcoding: $TRANSCODE_COUNT"
|
||||
echo " $ICON_EMBY Active streams: $ACTIVE_COUNT"
|
||||
echo " $ICON_EMBY Direct play: $DIRECT_NOW"
|
||||
echo " $ICON_EMBY Transcoding: $TRANSCODE_NOW"
|
||||
|
||||
if [[ "$ACTIVE_COUNT" -gt 0 ]]; then
|
||||
echo ""
|
||||
echo " Now playing:"
|
||||
echo "$SESSIONS" | jq -r '
|
||||
.[] |
|
||||
select(.NowPlayingItem != null) |
|
||||
" \(.UserName // "Unknown") → \(.NowPlayingItem.Name // "Unknown") [\(if .TranscodingInfo != null then "transcode" else "direct" end)]"
|
||||
' 2>/dev/null || true
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── Library Stats ────────────────────────────────────────────────────────────────────────────
|
||||
# ── Library Stats ─────────────────────────────────────────────────────────────────────────────
|
||||
echo "━━━ $ICON_EMBY Library ━━━"
|
||||
ITEMS=$(emby_api "Items/Counts" 2>/dev/null) || { warn "Could not fetch library counts"; ITEMS="{}"; }
|
||||
|
||||
@@ -139,31 +201,105 @@ echo " $ICON_EMBY Episodes: $EPISODE_COUNT"
|
||||
echo " $ICON_EMBY Songs: $SONG_COUNT"
|
||||
echo ""
|
||||
|
||||
# ── Ramdisk Status (from state file) ────────────────────────────────────────────────────────
|
||||
echo "━━━ $ICON_RAM Transcode Location ━━━"
|
||||
if mountpoint -q "$RAMDISK_PATH" 2>/dev/null; then
|
||||
RAMDISK_USED_KB=$(df "$RAMDISK_PATH" --output=used | tail -1 | tr -d ' ')
|
||||
RAMDISK_USED_GB=$(awk "BEGIN {printf \"%.2f\", $RAMDISK_USED_KB / 1048576}")
|
||||
SYMLINK=$(readlink "$TRANSCODE_LINK" 2>/dev/null || echo "unknown")
|
||||
echo " $ICON_RAM Ramdisk usage: ${RAMDISK_USED_GB}GB"
|
||||
echo " $ICON_LINK Symlink target: $SYMLINK"
|
||||
# ── Activity History ──────────────────────────────────────────────────────────────────────────
|
||||
# Query activity log for the configured period
|
||||
echo "━━━ $ICON_EMBY Activity — Last ${EMBY_REPORT_DAYS} Days ━━━"
|
||||
|
||||
REPORT_START=$(date -d "${EMBY_REPORT_DAYS} days ago" '+%Y-%m-%dT00:00:00.000Z')
|
||||
|
||||
ACTIVITY=$(emby_api "System/ActivityLog/Entries?MinDate=${REPORT_START}&Limit=1000" \
|
||||
2>/dev/null) || { warn "Could not fetch activity log"; ACTIVITY="{}"; }
|
||||
|
||||
TOTAL_PLAYS=$(echo "$ACTIVITY" | \
|
||||
jq '[.Items // [] | .[] | select(.Type == "VideoPlayback" or .Type == "AudioPlayback")] | length' \
|
||||
2>/dev/null || echo 0)
|
||||
|
||||
TRANSCODE_PLAYS=$(echo "$ACTIVITY" | \
|
||||
jq '[.Items // [] | .[] | select(.Type == "VideoPlaybackUnplugged" or
|
||||
(.Type == "VideoPlayback" and (.Overview // "" | contains("Transcode"))))] | length' \
|
||||
2>/dev/null || echo 0)
|
||||
|
||||
echo " $ICON_EMBY Total play events: $TOTAL_PLAYS"
|
||||
|
||||
if [[ "$TOTAL_PLAYS" -gt 0 ]]; then
|
||||
TRANSCODE_PCT=$(awk "BEGIN {printf \"%.0f\", ($TRANSCODE_PLAYS / $TOTAL_PLAYS) * 100}")
|
||||
DIRECT_PCT=$(( 100 - TRANSCODE_PCT ))
|
||||
echo " $ICON_EMBY Direct play: ~${DIRECT_PCT}%"
|
||||
echo " $ICON_EMBY Transcoded: ~${TRANSCODE_PCT}%"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── Top Content ───────────────────────────────────────────────────────────────────────────────
|
||||
echo "━━━ $ICON_EMBY Top ${EMBY_REPORT_TOP_N} Content ━━━"
|
||||
|
||||
TOP_ITEMS=$(emby_api "Items?SortBy=DatePlayed&SortOrder=Descending&Limit=${EMBY_REPORT_TOP_N}&Recursive=true&Fields=Overview&IncludeItemTypes=Movie,Episode" \
|
||||
2>/dev/null) || { warn "Could not fetch top content"; TOP_ITEMS="{}"; }
|
||||
|
||||
TOP_COUNT=$(echo "$TOP_ITEMS" | jq '.Items // [] | length' 2>/dev/null || echo 0)
|
||||
if [[ "$TOP_COUNT" -gt 0 ]]; then
|
||||
echo "$TOP_ITEMS" | jq -r '
|
||||
.Items // [] |
|
||||
to_entries[] |
|
||||
" \(.key + 1). \(.value.Name // "Unknown") [\(.value.Type // "")]"
|
||||
' 2>/dev/null || warn "Could not parse top content"
|
||||
else
|
||||
echo " $ICON_RAM Ramdisk: not mounted"
|
||||
echo " No recent play history found"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── Most Active Users ─────────────────────────────────────────────────────────────────────────
|
||||
echo "━━━ $ICON_EMBY Most Active Users ━━━"
|
||||
USERS=$(emby_api "Users" 2>/dev/null) || { warn "Could not fetch users"; USERS="[]"; }
|
||||
|
||||
USER_COUNT=$(echo "$USERS" | jq 'length' 2>/dev/null || echo 0)
|
||||
echo " $ICON_EMBY Total users: $USER_COUNT"
|
||||
|
||||
if [[ "$USER_COUNT" -gt 0 ]]; then
|
||||
echo "$USERS" | jq -r '
|
||||
sort_by(.LastActivityDate // "0") |
|
||||
reverse |
|
||||
.[:5][] |
|
||||
" \(.Name // "Unknown") — last active: \(.LastActivityDate // "never" | split("T")[0])"
|
||||
' 2>/dev/null || true
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── Ramdisk / Transcode Status ────────────────────────────────────────────────────────────────
|
||||
echo "━━━ $ICON_RAM Transcode Status ━━━"
|
||||
if mountpoint -q "$RAMDISK_PATH" 2>/dev/null; then
|
||||
RAMDISK_USED_KB=$(df "$RAMDISK_PATH" --output=used 2>/dev/null | tail -1 | tr -d ' ')
|
||||
RAMDISK_USED_GB=$(awk "BEGIN {printf \"%.2f\", ${RAMDISK_USED_KB:-0} / 1048576}")
|
||||
SYMLINK=$(readlink "$TRANSCODE_LINK" 2>/dev/null || echo "unknown")
|
||||
echo " $ICON_RAM Ramdisk usage: ${RAMDISK_USED_GB}GB / ${RAMDISK_SIZE:-8G}"
|
||||
echo " $ICON_LINK Symlink target: $SYMLINK"
|
||||
if [[ "$SYMLINK" == *"ssd"* ]] || [[ "$SYMLINK" == *"cache"* ]]; then
|
||||
warn "Transcode link pointing at SSD — ramdisk may be full"
|
||||
fi
|
||||
else
|
||||
warn "Ramdisk not mounted at $RAMDISK_PATH"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
END=$(date +%s)
|
||||
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# ━━━ $ICON_SUMMARY Summary ━━━
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# ==============================================================================================
|
||||
# ━━━ Summary ━━━
|
||||
# ==============================================================================================
|
||||
echo "━━━━━ $ICON_SUMMARY EMBY REPORT SUMMARY ━━━━━"
|
||||
echo "$ICON_EMBY Server: $SERVER_NAME (v$SERVER_VERSION)"
|
||||
echo "$ICON_EMBY Active: $ACTIVE_COUNT streams ($DIRECT_COUNT direct / $TRANSCODE_COUNT transcode)"
|
||||
echo "$ICON_EMBY Library: $MOVIE_COUNT movies $EPISODE_COUNT episodes $SONG_COUNT songs"
|
||||
echo "$ICON_TIME Duration: $(format_duration $((END - START)))"
|
||||
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
|
||||
echo "$ICON_EMBY Server: $SERVER_NAME (v$SERVER_VERSION)"
|
||||
echo "$ICON_EMBY Active: $ACTIVE_COUNT streams ($DIRECT_NOW direct / $TRANSCODE_NOW transcode)"
|
||||
echo "$ICON_EMBY Library: $MOVIE_COUNT movies $EPISODE_COUNT episodes $SONG_COUNT songs"
|
||||
echo "$ICON_EMBY Period: $TOTAL_PLAYS play events in last ${EMBY_REPORT_DAYS} days"
|
||||
echo "$ICON_TIME Duration: $(format_duration $(( END - START )))"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# Only notify on issues — high transcode rate may indicate config problem
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
notify "Emby report on $(hostname) — $ACTIVE_COUNT active streams ($DIRECT_COUNT direct / $TRANSCODE_COUNT transcode) — Library: $MOVIE_COUNT movies $EPISODE_COUNT episodes" "Emby Report" "normal"
|
||||
fi
|
||||
if [[ "$TOTAL_PLAYS" -gt 10 && "${TRANSCODE_PCT:-0}" -gt 80 ]]; then
|
||||
notify "Emby report on $(hostname) — high transcode rate: ${TRANSCODE_PCT}% of $TOTAL_PLAYS plays — check direct play config" \
|
||||
"Emby Report" "warning"
|
||||
fi
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user