mirror of
https://github.com/myronblair/jarvis
synced 2026-06-30 17:50:23 -05:00
c29d1bf4c7
- Proactive alerts (#1): polls every 60s; baselines on load so old alerts are silent; speaks new critical/warning alerts aloud if voice active; adds chat bubble for all new alerts - Jellyfin control (#2): pause/stop/next/previous via voice — auto-detects active session; 12 KB intents; jellyfin.php control action uses Jellyfin General Commands API - Agent sparklines (#6): metrics.php returns 2h CPU+MEM history per agent; SVG polyline sparklines rendered in each agent card (cyan=CPU, green=MEM); non-blocking fetch so existing view shows instantly - Agent health CCR (#7): updated hourly cloud routine to current 13-agent roster, removed ollama-ai and alien-pc, added all active agents with correct IPs/IDs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
110 lines
4.6 KiB
PHP
110 lines
4.6 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../config.php';
|
|
require_once __DIR__ . '/../../includes/auth.php';
|
|
|
|
header('Content-Type: application/json');
|
|
AuthMiddleware::requireAuth();
|
|
|
|
$action = $_GET['action'] ?? 'sessions';
|
|
|
|
function jf_get(string $path): array {
|
|
$url = JELLYFIN_URL . $path . (str_contains($path, '?') ? '&' : '?') . 'api_key=' . JELLYFIN_API_KEY;
|
|
$ctx = stream_context_create(['http' => ['timeout' => 5, 'ignore_errors' => true]]);
|
|
$raw = @file_get_contents($url, false, $ctx);
|
|
return $raw ? (json_decode($raw, true) ?? []) : [];
|
|
}
|
|
|
|
switch ($action) {
|
|
case 'sessions':
|
|
$sessions = jf_get('/Sessions');
|
|
$active = array_filter($sessions, fn($s) => isset($s['NowPlayingItem']));
|
|
$out = [];
|
|
foreach ($active as $s) {
|
|
$np = $s['NowPlayingItem'];
|
|
$pos = $s['PlayState']['PositionTicks'] ?? 0;
|
|
$dur = $np['RunTimeTicks'] ?? 0;
|
|
$out[] = [
|
|
'session_id' => $s['Id'],
|
|
'user' => $s['UserName'] ?? 'Unknown',
|
|
'device' => $s['DeviceName'] ?? '',
|
|
'client' => $s['Client'] ?? '',
|
|
'title' => $np['Name'] ?? '',
|
|
'type' => $np['Type'] ?? '',
|
|
'series' => $np['SeriesName'] ?? null,
|
|
'year' => $np['ProductionYear'] ?? null,
|
|
'paused' => $s['PlayState']['IsPaused'] ?? false,
|
|
'position_pct'=> $dur > 0 ? round($pos / $dur * 100) : 0,
|
|
];
|
|
}
|
|
echo json_encode(['sessions' => array_values($out), 'total_active' => count($out)]);
|
|
break;
|
|
|
|
case 'library':
|
|
$movies = jf_get('/Items?IncludeItemTypes=Movie&Recursive=true&Limit=0');
|
|
$series = jf_get('/Items?IncludeItemTypes=Series&Recursive=true&Limit=0');
|
|
$episodes= jf_get('/Items?IncludeItemTypes=Episode&Recursive=true&Limit=0');
|
|
echo json_encode([
|
|
'movies' => $movies['TotalRecordCount'] ?? 0,
|
|
'series' => $series['TotalRecordCount'] ?? 0,
|
|
'episodes' => $episodes['TotalRecordCount'] ?? 0,
|
|
]);
|
|
break;
|
|
|
|
case 'search':
|
|
$q = trim($_GET['q'] ?? '');
|
|
if (!$q) { echo json_encode(['results' => []]); break; }
|
|
$data = jf_get('/Search/Hints?SearchTerm=' . urlencode($q) . '&Limit=8&IncludeItemTypes=Movie,Series,Episode');
|
|
$hints = $data['SearchHints'] ?? [];
|
|
$results = array_map(fn($h) => [
|
|
'id' => $h['ItemId'],
|
|
'name' => $h['Name'],
|
|
'type' => $h['Type'],
|
|
'year' => $h['ProductionYear'] ?? null,
|
|
'series'=> $h['Series'] ?? null,
|
|
], $hints);
|
|
echo json_encode(['results' => $results]);
|
|
break;
|
|
|
|
case 'recent':
|
|
$data = jf_get('/Items/Latest?Limit=8&IncludeItemTypes=Movie,Episode&Fields=Overview');
|
|
$out = array_map(fn($i) => [
|
|
'name' => $i['Name'],
|
|
'type' => $i['Type'],
|
|
'series' => $i['SeriesName'] ?? null,
|
|
'year' => $i['ProductionYear'] ?? null,
|
|
], is_array($data) ? $data : []);
|
|
echo json_encode(['recent' => $out]);
|
|
break;
|
|
|
|
case 'control':
|
|
$jfSessionId = $_GET['session_id'] ?? '';
|
|
$jfCommand = $_GET['command'] ?? '';
|
|
// General commands supported by Jellyfin session control
|
|
$allowed = ['TogglePause', 'Stop', 'NextTrack', 'PreviousTrack', 'VolumeUp', 'VolumeDown'];
|
|
if (!$jfSessionId || !in_array($jfCommand, $allowed, true)) {
|
|
// No session_id: get the first active session automatically
|
|
if (!$jfSessionId && in_array($jfCommand, $allowed, true)) {
|
|
$sessions = jf_get('/Sessions');
|
|
foreach ($sessions as $s) {
|
|
if (isset($s['NowPlayingItem'])) { $jfSessionId = $s['Id']; break; }
|
|
}
|
|
}
|
|
if (!$jfSessionId) { echo json_encode(['error' => 'No active session or invalid command']); break; }
|
|
}
|
|
$url = JELLYFIN_URL . '/Sessions/' . rawurlencode($jfSessionId) . '/Command/' . $jfCommand
|
|
. '?api_key=' . JELLYFIN_API_KEY;
|
|
$ctx = stream_context_create(['http' => [
|
|
'method' => 'POST',
|
|
'timeout' => 5,
|
|
'header' => 'Content-Type: application/json',
|
|
'content' => '{}',
|
|
'ignore_errors' => true,
|
|
]]);
|
|
@file_get_contents($url, false, $ctx);
|
|
echo json_encode(['success' => true, 'command' => $jfCommand, 'session_id' => $jfSessionId]);
|
|
break;
|
|
|
|
default:
|
|
echo json_encode(['error' => 'Unknown action']);
|
|
}
|