mirror of
https://github.com/myronblair/jarvis
synced 2026-06-30 17:50:23 -05:00
feat: voice routing, Jellyfin integration, context-aware briefing
- Voice routing (#2): focus_mode and show_panels KB intents → chat.php → ui_action response field → index.html dispatch; removed local JS regex intercepts - Jellyfin (#3): jellyfin.php endpoint (sessions/library/search/recent), JELLYFIN_URL/API_KEY in config.php, api.php router, now_playing/library KB intents in chat.php - Daily briefing (#4): time-of-day greeting (morning/afternoon/evening), weather lead from api_cache, offline agent count summary Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
<?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;
|
||||
|
||||
default:
|
||||
echo json_encode(['error' => 'Unknown action']);
|
||||
}
|
||||
Reference in New Issue
Block a user