|
|
@@ -0,0 +1,801 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+trait EmbyLiveTVTrackerHomepageItem
|
|
|
+{
|
|
|
+ public function embyLiveTVTrackerSettingsArray($infoOnly = false)
|
|
|
+ {
|
|
|
+ $homepageInformation = [
|
|
|
+ 'name' => 'EmbyLiveTVTracker',
|
|
|
+ 'enabled' => strpos('personal', $this->config['license']) !== false,
|
|
|
+ 'image' => 'plugins/images/homepage/embyLiveTVTracker.png',
|
|
|
+ 'category' => 'Media Server',
|
|
|
+ 'settingsArray' => __FUNCTION__
|
|
|
+ ];
|
|
|
+ if ($infoOnly) {
|
|
|
+ return $homepageInformation;
|
|
|
+ }
|
|
|
+ $homepageSettings = [
|
|
|
+ 'debug' => true,
|
|
|
+ 'settings' => [
|
|
|
+ 'Enable' => [
|
|
|
+ $this->settingsOption('enable', 'homepageEmbyLiveTVTrackerEnabled'),
|
|
|
+ $this->settingsOption('auth', 'homepageEmbyLiveTVTrackerAuth'),
|
|
|
+ ],
|
|
|
+ 'Connection' => [
|
|
|
+ $this->settingsOption('url', 'embyURL'),
|
|
|
+ $this->settingsOption('token', 'embyToken'),
|
|
|
+ $this->settingsOption('disable-cert-check', 'embyDisableCertCheck'),
|
|
|
+ $this->settingsOption('use-custom-certificate', 'embyUseCustomCertificate'),
|
|
|
+ ],
|
|
|
+ 'Display Options' => [
|
|
|
+ $this->settingsOption('number', 'homepageEmbyLiveTVTrackerRefresh', ['label' => 'Auto-refresh Interval (minutes)', 'min' => 1, 'max' => 60]),
|
|
|
+ $this->settingsOption('switch', 'homepageEmbyLiveTVTrackerCompactView', ['label' => 'Use Compact View']),
|
|
|
+ $this->settingsOption('switch', 'homepageEmbyLiveTVTrackerShowDuration', ['label' => 'Show Recording Duration']),
|
|
|
+ $this->settingsOption('switch', 'homepageEmbyLiveTVTrackerShowSeriesInfo', ['label' => 'Show Series Information']),
|
|
|
+ $this->settingsOption('switch', 'homepageEmbyLiveTVTrackerShowUserInfo', ['label' => 'Show User Information']),
|
|
|
+ $this->settingsOption('number', 'homepageEmbyLiveTVTrackerMaxItems', ['label' => 'Maximum Scheduled Items', 'min' => 5, 'max' => 50]),
|
|
|
+ $this->settingsOption('switch', 'homepageEmbyLiveTVTrackerShowCompleted', ['label' => 'Show Completed Recordings']),
|
|
|
+ $this->settingsOption('number', 'homepageEmbyLiveTVTrackerDaysShown', ['label' => 'Days of Completed Recordings', 'min' => 1, 'max' => 30]),
|
|
|
+ $this->settingsOption('number', 'homepageEmbyLiveTVTrackerMaxCompletedItems', ['label' => 'Maximum Completed Items', 'min' => 5, 'max' => 50]),
|
|
|
+ $this->settingsOption('switch', 'homepageEmbyLiveTVTrackerDebug', ['label' => 'Enable Debug Logging']),
|
|
|
+ ],
|
|
|
+ 'Test Connection' => [
|
|
|
+ $this->settingsOption('blank', null, ['label' => 'Please Save before Testing']),
|
|
|
+ $this->settingsOption('test', 'embyLiveTVTracker'),
|
|
|
+ ]
|
|
|
+ ]
|
|
|
+ ];
|
|
|
+ return array_merge($homepageInformation, $homepageSettings);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function testConnectionEmbyLiveTVTracker()
|
|
|
+ {
|
|
|
+ if (!$this->homepageItemPermissions($this->embyLiveTVTrackerHomepagePermissions('test'), true)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ $url = $this->qualifyURL($this->config['embyURL']);
|
|
|
+ $url = $url . "/emby/System/Info?api_key=" . $this->config['embyToken'];
|
|
|
+ $options = $this->requestOptions($url, null, $this->config['embyDisableCertCheck'], $this->config['embyUseCustomCertificate']);
|
|
|
+ try {
|
|
|
+ $response = Requests::get($url, [], $options);
|
|
|
+ if ($response->success) {
|
|
|
+ $info = json_decode($response->body, true);
|
|
|
+ if (isset($info['ServerName'])) {
|
|
|
+ // Test LiveTV functionality
|
|
|
+ $liveTvUrl = $this->qualifyURL($this->config['embyURL']) . '/emby/LiveTv/Info?api_key=' . $this->config['embyToken'];
|
|
|
+ try {
|
|
|
+ $liveTvResponse = Requests::get($liveTvUrl, [], $options);
|
|
|
+ $liveTvInfo = json_decode($liveTvResponse->body, true);
|
|
|
+ $hasLiveTV = isset($liveTvInfo['Services']) && count($liveTvInfo['Services']) > 0;
|
|
|
+ $message = 'Successfully connected to ' . $info['ServerName'];
|
|
|
+ if ($hasLiveTV) {
|
|
|
+ $message .= ' with LiveTV support enabled';
|
|
|
+ } else {
|
|
|
+ $message .= ' (Warning: LiveTV may not be configured)';
|
|
|
+ }
|
|
|
+ $this->setAPIResponse('success', $message, 200);
|
|
|
+ } catch (Exception $e) {
|
|
|
+ $this->setAPIResponse('success', 'Connected to ' . $info['ServerName'] . ' but LiveTV status unknown', 200);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ $this->setAPIResponse('error', 'Invalid response from Emby server', 500);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ $this->setAPIResponse('error', 'Emby Connection Error', 500);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ } catch (Requests_Exception $e) {
|
|
|
+ $this->setResponse(500, $e->getMessage());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function embyLiveTVTrackerHomepagePermissions($key = null)
|
|
|
+ {
|
|
|
+ $permissions = [
|
|
|
+ 'test' => [
|
|
|
+ 'enabled' => [
|
|
|
+ 'homepageEmbyLiveTVTrackerEnabled',
|
|
|
+ ],
|
|
|
+ 'auth' => [
|
|
|
+ 'homepageEmbyLiveTVTrackerAuth',
|
|
|
+ ],
|
|
|
+ 'not_empty' => [
|
|
|
+ 'embyURL',
|
|
|
+ 'embyToken'
|
|
|
+ ]
|
|
|
+ ],
|
|
|
+ 'main' => [
|
|
|
+ 'enabled' => [
|
|
|
+ 'homepageEmbyLiveTVTrackerEnabled'
|
|
|
+ ],
|
|
|
+ 'auth' => [
|
|
|
+ 'homepageEmbyLiveTVTrackerAuth'
|
|
|
+ ],
|
|
|
+ 'not_empty' => [
|
|
|
+ 'embyURL',
|
|
|
+ 'embyToken'
|
|
|
+ ]
|
|
|
+ ]
|
|
|
+ ];
|
|
|
+ return $this->homepageCheckKeyPermissions($key, $permissions);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function homepageOrderEmbyLiveTVTracker()
|
|
|
+ {
|
|
|
+ if ($this->homepageItemPermissions($this->embyLiveTVTrackerHomepagePermissions('main'))) {
|
|
|
+ $refreshInterval = ($this->config['homepageEmbyLiveTVTrackerRefresh'] ?? 5) * 60000; // Convert minutes to milliseconds
|
|
|
+ $compactView = ($this->config['homepageEmbyLiveTVTrackerCompactView'] ?? false) ? 'true' : 'false';
|
|
|
+ $showDuration = ($this->config['homepageEmbyLiveTVTrackerShowDuration'] ?? true) ? 'true' : 'false';
|
|
|
+ $showSeriesInfo = ($this->config['homepageEmbyLiveTVTrackerShowSeriesInfo'] ?? true) ? 'true' : 'false';
|
|
|
+ $showUserInfo = ($this->config['homepageEmbyLiveTVTrackerShowUserInfo'] ?? false) ? 'true' : 'false';
|
|
|
+ $maxItems = $this->config['homepageEmbyLiveTVTrackerMaxItems'] ?? 10;
|
|
|
+ $showCompleted = ($this->config['homepageEmbyLiveTVTrackerShowCompleted'] ?? true) ? 'true' : 'false';
|
|
|
+ $daysShown = $this->config['homepageEmbyLiveTVTrackerDaysShown'] ?? 7;
|
|
|
+ $maxCompletedItems = $this->config['homepageEmbyLiveTVTrackerMaxCompletedItems'] ?? 5;
|
|
|
+
|
|
|
+ $panelClass = ($compactView === 'true') ? 'panel-compact' : '';
|
|
|
+ $statsClass = ($compactView === 'true') ? 'col-sm-6' : 'col-sm-3';
|
|
|
+
|
|
|
+ return '
|
|
|
+ <div id="' . __FUNCTION__ . '">
|
|
|
+ <div class="white-box ' . $panelClass . '">
|
|
|
+ <div class="white-box-header">
|
|
|
+ <i class="fa fa-tv"></i> Emby LiveTV Tracker
|
|
|
+ <span class="pull-right">
|
|
|
+ <small id="embylivetv-last-update" class="text-muted"></small>
|
|
|
+ <button class="btn btn-xs btn-primary" onclick="refreshEmbyLiveTVData()" title="Refresh Data">
|
|
|
+ <i class="fa fa-refresh" id="embylivetv-refresh-icon"></i>
|
|
|
+ </button>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div class="white-box-content">
|
|
|
+ <div class="row" id="embylivetv-stats">
|
|
|
+ <div class="' . $statsClass . '">
|
|
|
+ <div class="text-center">
|
|
|
+ <h3 id="embylivetv-active-timers" class="text-success">-</h3>
|
|
|
+ <small>Active Timers</small>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="' . $statsClass . '">
|
|
|
+ <div class="text-center">
|
|
|
+ <h3 id="embylivetv-series-timers" class="text-info">-</h3>
|
|
|
+ <small>Series Timers</small>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ' . (($compactView === 'false') ? '
|
|
|
+ <div class="' . $statsClass . '">
|
|
|
+ <div class="text-center">
|
|
|
+ <h3 id="embylivetv-today-recordings" class="text-warning">-</h3>
|
|
|
+ <small>Today\'s Recordings</small>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="' . $statsClass . '">
|
|
|
+ <div class="text-center">
|
|
|
+ <h3 id="embylivetv-total-recordings" class="text-primary">-</h3>
|
|
|
+ <small>Total Recordings</small>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ' : '') . '
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Scheduled Recordings Table -->
|
|
|
+ <div class="row" style="margin-top: 20px;">
|
|
|
+ <div class="col-lg-12">
|
|
|
+ <h4>
|
|
|
+ Scheduled Recordings
|
|
|
+ <small class="text-muted">Upcoming and active timers</small>
|
|
|
+ </h4>
|
|
|
+ <div class="table-responsive">
|
|
|
+ <table class="table table-hover table-striped table-condensed">
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th width="120">Date</th>
|
|
|
+ <th>Series</th>
|
|
|
+ ' . (($showSeriesInfo === 'true') ? '<th>Episode Title</th>' : '') . '
|
|
|
+ <th>Channel</th>
|
|
|
+ ' . (($showUserInfo === 'true') ? '<th>User</th>' : '') . '
|
|
|
+ ' . (($showDuration === 'true') ? '<th width="80">Duration</th>' : '') . '
|
|
|
+ <th width="70">Status</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody id="embylivetv-scheduled-table">
|
|
|
+ <tr>
|
|
|
+ <td colspan="' . (4 + ($showSeriesInfo === 'true' ? 1 : 0) + ($showUserInfo === 'true' ? 1 : 0) + ($showDuration === 'true' ? 1 : 0)) . '" class="text-center">
|
|
|
+ <i class="fa fa-spinner fa-spin"></i> Loading...
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Completed Recordings Table (only if enabled) -->
|
|
|
+ ' . (($showCompleted === 'true') ? '
|
|
|
+ <div class="row" style="margin-top: 20px;">
|
|
|
+ <div class="col-lg-12">
|
|
|
+ <h4>
|
|
|
+ Completed Recordings
|
|
|
+ <small class="text-muted">Recent recordings</small>
|
|
|
+ </h4>
|
|
|
+ <div class="table-responsive">
|
|
|
+ <table class="table table-hover table-striped table-condensed">
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th width="120">Date</th>
|
|
|
+ <th>Series</th>
|
|
|
+ ' . (($showSeriesInfo === 'true') ? '<th>Series</th>' : '') . '
|
|
|
+ ' . (($showDuration === 'true') ? '<th width="80">Duration</th>' : '') . '
|
|
|
+ <th width="70">Status</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody id="embylivetv-completed-table">
|
|
|
+ <tr>
|
|
|
+ <td colspan="' . (3 + ($showSeriesInfo === 'true' ? 1 : 0) + ($showDuration === 'true' ? 1 : 0)) . '" class="text-center">
|
|
|
+ <i class="fa fa-spinner fa-spin"></i> Loading...
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ' : '') . '
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <style>
|
|
|
+ .panel-compact .white-box-content { padding: 10px; }
|
|
|
+ .panel-compact h3 { margin: 5px 0; font-size: 1.8em; }
|
|
|
+ .panel-compact small { font-size: 0.85em; }
|
|
|
+ #' . __FUNCTION__ . ' .table-condensed td { padding: 4px 8px; font-size: 0.9em; }
|
|
|
+ #' . __FUNCTION__ . ' .status-success { color: #5cb85c; }
|
|
|
+ #' . __FUNCTION__ . ' .status-recording { color: #d9534f; }
|
|
|
+ #' . __FUNCTION__ . ' .status-scheduled { color: #f0ad4e; }
|
|
|
+ </style>
|
|
|
+
|
|
|
+ <script>
|
|
|
+ var embyLiveTVRefreshTimer;
|
|
|
+ var embyLiveTVLastRefresh = 0;
|
|
|
+
|
|
|
+ function refreshEmbyLiveTVData() {
|
|
|
+ var refreshIcon = $("#embylivetv-refresh-icon");
|
|
|
+ refreshIcon.addClass("fa-spin");
|
|
|
+
|
|
|
+ // Show loading state
|
|
|
+ $("#embylivetv-stats h3").text("-");
|
|
|
+ $("#embylivetv-scheduled-table").html("<tr><td colspan=\"' . (4 + ($showSeriesInfo === 'true' ? 1 : 0) + ($showUserInfo === 'true' ? 1 : 0) + ($showDuration === 'true' ? 1 : 0)) . '\" class=\"text-center\"><i class=\"fa fa-spinner fa-spin\"></i> Loading...</td></tr>");
|
|
|
+ ' . (($showCompleted === 'true') ? '$("#embylivetv-completed-table").html("<tr><td colspan=\"' . (4 + ($showSeriesInfo === 'true' ? 1 : 0) + ($showUserInfo === 'true' ? 1 : 0) + ($showDuration === 'true' ? 1 : 0)) . '\" class=\"text-center\"><i class=\"fa fa-spinner fa-spin\"></i> Loading...</td></tr>");' : '') . ';
|
|
|
+
|
|
|
+ // Load stats and activity
|
|
|
+ homepageEmbyLiveTVTrackerStats()
|
|
|
+ .always(function() {
|
|
|
+ refreshIcon.removeClass("fa-spin");
|
|
|
+ embyLiveTVLastRefresh = Date.now();
|
|
|
+ updateEmbyLiveTVLastRefreshTime();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function updateEmbyLiveTVLastRefreshTime() {
|
|
|
+ if (embyLiveTVLastRefresh > 0) {
|
|
|
+ var ago = Math.floor((Date.now() - embyLiveTVLastRefresh) / 1000);
|
|
|
+ var timeText = ago < 60 ? ago + "s ago" : Math.floor(ago / 60) + "m ago";
|
|
|
+ $("#embylivetv-last-update").text("Updated " + timeText);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function homepageEmbyLiveTVTrackerStats() {
|
|
|
+ return organizrAPI2("GET", "api/v2/homepage/embyLiveTVTracker/stats")
|
|
|
+ .done(function(data) {
|
|
|
+ console.log("Stats response received:", data);
|
|
|
+ if (data && data.response && data.response.result === "success" && data.response.data) {
|
|
|
+ console.log("Stats data is valid, loading activity...");
|
|
|
+ $("#embylivetv-active-timers").text(data.response.data.activeTimers || "0");
|
|
|
+ $("#embylivetv-series-timers").text(data.response.data.seriesTimers || "0");
|
|
|
+ ' . (($compactView === 'false') ? '
|
|
|
+ $("#embylivetv-today-recordings").text(data.response.data.todaysRecordings || "0");
|
|
|
+ $("#embylivetv-total-recordings").text(data.response.data.totalRecordings || "0");
|
|
|
+ ' : '') . '
|
|
|
+
|
|
|
+ // Load activity
|
|
|
+ homepageEmbyLiveTVTrackerActivity();
|
|
|
+ } else {
|
|
|
+ console.error("Stats response structure issue:", {
|
|
|
+ hasData: !!data,
|
|
|
+ hasResponse: !!(data && data.response),
|
|
|
+ result: data && data.response && data.response.result,
|
|
|
+ hasResponseData: !!(data && data.response && data.response.data)
|
|
|
+ });
|
|
|
+ console.error("Failed to load Emby LiveTV stats:", data.response ? data.response.message : "Unknown error");
|
|
|
+ $("#embylivetv-stats h3").text("?").attr("title", "Error loading data");
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .fail(function(xhr, status, error) {
|
|
|
+ console.error("Error loading Emby LiveTV stats:", error);
|
|
|
+ $("#embylivetv-stats h3").text("!").attr("title", "Connection failed");
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function homepageEmbyLiveTVTrackerActivity() {
|
|
|
+ console.log("Activity function called - making API request...");
|
|
|
+ return organizrAPI2("GET", "api/v2/homepage/embyLiveTVTracker/activity?days=' . ($daysShown ?: 7) . '\u0026limit=' . ($maxItems ?: 10) . '")
|
|
|
+ .done(function(data) {
|
|
|
+ console.log("Activity response received:", data);
|
|
|
+ console.log("Response structure check:", {
|
|
|
+ hasData: !!data,
|
|
|
+ hasResponse: !!(data && data.response),
|
|
|
+ result: data && data.response && data.response.result,
|
|
|
+ hasResponseData: !!(data && data.response && data.response.data),
|
|
|
+ hasActivities: !!(data && data.response && data.response.data && data.response.data.activities),
|
|
|
+ activitiesLength: data && data.response && data.response.data && data.response.data.activities ? data.response.data.activities.length : 0
|
|
|
+ });
|
|
|
+ if (data && data.response && data.response.result === "success" && data.response.data) {
|
|
|
+ var scheduledRecordings = data.response.data.scheduledRecordings || [];
|
|
|
+ var completedRecordings = data.response.data.completedRecordings || [];
|
|
|
+
|
|
|
+ console.log("Scheduled recordings:", scheduledRecordings.length);
|
|
|
+ console.log("Completed recordings:", completedRecordings.length);
|
|
|
+
|
|
|
+ // Apply limits to each category separately to ensure we show both types
|
|
|
+ var maxScheduled = Math.floor(' . $maxItems . ' * 0.7); // 70% for scheduled
|
|
|
+ var maxCompleted = ' . $maxItems . ' - maxScheduled; // 30% for completed
|
|
|
+
|
|
|
+ // If we have fewer scheduled than the 70% allocation, give more space to completed
|
|
|
+ if (scheduledRecordings.length < maxScheduled) {
|
|
|
+ maxCompleted = Math.min(completedRecordings.length, ' . $maxItems . ' - scheduledRecordings.length);
|
|
|
+ }
|
|
|
+ // If we have fewer completed than the 30% allocation, give more space to scheduled
|
|
|
+ if (completedRecordings.length < maxCompleted) {
|
|
|
+ maxScheduled = Math.min(scheduledRecordings.length, ' . $maxItems . ' - completedRecordings.length);
|
|
|
+ }
|
|
|
+
|
|
|
+ var scheduledActivities = scheduledRecordings.slice(0, maxScheduled);
|
|
|
+ var completedActivities = completedRecordings.slice(0, maxCompleted);
|
|
|
+
|
|
|
+ console.log("Split - Scheduled activities:", scheduledActivities.length);
|
|
|
+ console.log("Split - Completed activities:", completedActivities.length);
|
|
|
+
|
|
|
+ // Helper function to format scheduled activity rows
|
|
|
+ function formatScheduledRow(activity) {
|
|
|
+ var date = new Date(activity.date);
|
|
|
+ var formattedDate = date.toLocaleDateString() + " " + date.toLocaleTimeString([], {hour: "2-digit", minute:"2-digit"});
|
|
|
+ var status = activity.status || "Scheduled";
|
|
|
+ var statusClass = status.toLowerCase() === "completed" ? "status-success" :
|
|
|
+ status.toLowerCase() === "recording" ? "status-recording" : "status-scheduled";
|
|
|
+
|
|
|
+ return "<tr>" +
|
|
|
+ "<td><small>" + formattedDate + "</small></td>" +
|
|
|
+ "<td>" + (activity.seriesName || activity.name || "-") + "</td>" +
|
|
|
+ ' . (($showSeriesInfo === 'true') ? '"<td><small>" + (activity.episodeTitle || activity.name || "-") + "</small></td>" +' : '') . '
|
|
|
+ "<td><small>" + (activity.channelName || "-") + "</small></td>" +
|
|
|
+ ' . (($showUserInfo === 'true') ? '"<td><small>" + (activity.userName || "-") + "</small></td>" +' : '') . '
|
|
|
+ ' . (($showDuration === 'true') ? '"<td><small>" + (activity.duration || "-") + "</small></td>" +' : '') . '
|
|
|
+ "<td><span class=\"" + statusClass + "\"><i class=\"fa fa-circle\"></i></span></td>" +
|
|
|
+ "</tr>";
|
|
|
+ }
|
|
|
+
|
|
|
+ // Helper function to format completed activity rows (no channel or user columns)
|
|
|
+ function formatCompletedRow(activity) {
|
|
|
+ var date = new Date(activity.date);
|
|
|
+ var formattedDate = date.toLocaleDateString() + " " + date.toLocaleTimeString([], {hour: "2-digit", minute:"2-digit"});
|
|
|
+ var status = activity.status || "Completed";
|
|
|
+ var statusClass = "status-success";
|
|
|
+
|
|
|
+ return "<tr>" +
|
|
|
+ "<td><small>" + formattedDate + "</small></td>" +
|
|
|
+ "<td>" + (activity.seriesName || activity.name || "-") + "</td>" +
|
|
|
+ ' . (($showSeriesInfo === 'true') ? '"<td><small>" + (activity.seriesName || "-") + "</small></td>" +' : '') . '
|
|
|
+ ' . (($showDuration === 'true') ? '"<td><small>" + (activity.duration || "-") + "</small></td>" +' : '') . '
|
|
|
+ "<td><span class=\"" + statusClass + "\"><i class=\"fa fa-circle\"></i></span></td>" +
|
|
|
+ "</tr>";
|
|
|
+ }
|
|
|
+
|
|
|
+ // Populate scheduled recordings table
|
|
|
+ var scheduledTable = $("#embylivetv-scheduled-table");
|
|
|
+ if (scheduledActivities.length === 0) {
|
|
|
+ scheduledTable.html("<tr><td colspan=\"' . (4 + ($showSeriesInfo === 'true' ? 1 : 0) + ($showDuration === 'true' ? 1 : 0) + ($showUserInfo === 'true' ? 1 : 0)) . '\" class=\"text-center text-muted\">No scheduled recordings</td></tr>");
|
|
|
+ } else {
|
|
|
+ var scheduledRows = scheduledActivities.map(formatScheduledRow).join("");
|
|
|
+ scheduledTable.html(scheduledRows);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Populate completed recordings table (only if enabled and table exists)
|
|
|
+ ' . (($showCompleted === 'true') ? '
|
|
|
+ var completedTable = $("#embylivetv-completed-table");
|
|
|
+ if (completedActivities.length === 0) {
|
|
|
+ completedTable.html("<tr><td colspan=\"' . (3 + ($showSeriesInfo === 'true' ? 1 : 0) + ($showDuration === 'true' ? 1 : 0)) . '\" class=\"text-center text-muted\">No completed recordings</td></tr>");
|
|
|
+ } else {
|
|
|
+ var completedRows = completedActivities.map(formatCompletedRow).join("");
|
|
|
+ completedTable.html(completedRows);
|
|
|
+ }
|
|
|
+ ' : '') . '
|
|
|
+ } else {
|
|
|
+ $("#embylivetv-scheduled-table").html("<tr><td colspan=\"' . (4 + ($showSeriesInfo === 'true' ? 1 : 0) + ($showDuration === 'true' ? 1 : 0) + ($showUserInfo === 'true' ? 1 : 0)) . '\" class=\"text-center text-muted\">No activity data available</td></tr>");
|
|
|
+ ' . (($showCompleted === 'true') ? '$("#embylivetv-completed-table").html("<tr><td colspan=\"' . (3 + ($showSeriesInfo === 'true' ? 1 : 0) + ($showDuration === 'true' ? 1 : 0)) . '\" class=\"text-center text-muted\">No activity data available</td></tr>");' : '') . '
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .fail(function(xhr, status, error) {
|
|
|
+ console.error("Error loading Emby LiveTV activity:", error);
|
|
|
+ $("#embylivetv-scheduled-table").html("<tr><td colspan=\"' . (4 + ($showSeriesInfo === 'true' ? 1 : 0) + ($showUserInfo === 'true' ? 1 : 0) + ($showDuration === 'true' ? 1 : 0)) . '\" class=\"text-center text-danger\">Error loading activity data</td></tr>");
|
|
|
+ ' . (($showCompleted === 'true') ? '$("#embylivetv-completed-table").html("<tr><td colspan=\"' . (3 + ($showSeriesInfo === 'true' ? 1 : 0) + ($showDuration === 'true' ? 1 : 0)) . '\" class=\"text-center text-danger\">Error loading activity data</td></tr>");' : '') . '
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // Auto-refresh setup
|
|
|
+ var refreshInterval = ' . $refreshInterval . ';
|
|
|
+ if (refreshInterval > 0) {
|
|
|
+ embyLiveTVRefreshTimer = setInterval(function() {
|
|
|
+ refreshEmbyLiveTVData();
|
|
|
+ }, refreshInterval);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Update time display every 30 seconds
|
|
|
+ setInterval(updateEmbyLiveTVLastRefreshTime, 30000);
|
|
|
+
|
|
|
+ // Initial load
|
|
|
+ $(document).ready(function() {
|
|
|
+ refreshEmbyLiveTVData();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Cleanup timer when page unloads
|
|
|
+ $(window).on("beforeunload", function() {
|
|
|
+ if (embyLiveTVRefreshTimer) {
|
|
|
+ clearInterval(embyLiveTVRefreshTimer);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ </script>
|
|
|
+ ';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function getHomepageEmbyLiveTVStats()
|
|
|
+ {
|
|
|
+ if (!$this->homepageItemPermissions($this->embyLiveTVTrackerHomepagePermissions('main'), true)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!$this->config['embyURL'] || !$this->config['embyToken']) {
|
|
|
+ $this->setAPIResponse('error', 'Emby URL or Token not configured', 500);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ $options = $this->requestOptions($this->config['embyURL'], null, $this->config['embyDisableCertCheck'], $this->config['embyUseCustomCertificate']);
|
|
|
+ $baseUrl = $this->qualifyURL($this->config['embyURL']);
|
|
|
+
|
|
|
+ $stats = [
|
|
|
+ 'activeTimers' => 0,
|
|
|
+ 'seriesTimers' => 0,
|
|
|
+ 'todaysRecordings' => 0,
|
|
|
+ 'totalRecordings' => 0,
|
|
|
+ 'recentRecordings' => []
|
|
|
+ ];
|
|
|
+
|
|
|
+ // Get active timers
|
|
|
+ $timersUrl = $baseUrl . '/emby/LiveTv/Timers?api_key=' . $this->config['embyToken'];
|
|
|
+ try {
|
|
|
+ $timersResponse = Requests::get($timersUrl, [], $options);
|
|
|
+ if ($timersResponse->success) {
|
|
|
+ $timers = json_decode($timersResponse->body, true);
|
|
|
+ $stats['activeTimers'] = count($timers['Items'] ?? []);
|
|
|
+ }
|
|
|
+ } catch (Exception $e) {
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->warning('Failed to get timers: ' . $e->getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get series timers
|
|
|
+ $seriesTimersUrl = $baseUrl . '/emby/LiveTv/SeriesTimers?api_key=' . $this->config['embyToken'];
|
|
|
+ try {
|
|
|
+ $seriesTimersResponse = Requests::get($seriesTimersUrl, [], $options);
|
|
|
+ if ($seriesTimersResponse->success) {
|
|
|
+ $seriesTimers = json_decode($seriesTimersResponse->body, true);
|
|
|
+ $stats['seriesTimers'] = count($seriesTimers['Items'] ?? []);
|
|
|
+ }
|
|
|
+ } catch (Exception $e) {
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->warning('Failed to get series timers: ' . $e->getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get recordings from the last 90 days
|
|
|
+ $recordingsUrl = $baseUrl . '/emby/LiveTv/Recordings?api_key=' . $this->config['embyToken'] . '&StartIndex=0&Limit=50&Fields=Overview,DateCreated&SortBy=DateCreated&SortOrder=Descending';
|
|
|
+ try {
|
|
|
+ $recordingsResponse = Requests::get($recordingsUrl, [], $options);
|
|
|
+ if ($recordingsResponse->success) {
|
|
|
+ $recordings = json_decode($recordingsResponse->body, true);
|
|
|
+ $allRecordings = $recordings['Items'] ?? [];
|
|
|
+
|
|
|
+ // Count today's recordings
|
|
|
+ $today = date('Y-m-d');
|
|
|
+ $todaysCount = 0;
|
|
|
+ $recentRecordings = [];
|
|
|
+
|
|
|
+ foreach ($allRecordings as $recording) {
|
|
|
+ if (isset($recording['DateCreated'])) {
|
|
|
+ $recordDate = date('Y-m-d', strtotime($recording['DateCreated']));
|
|
|
+ if ($recordDate === $today) {
|
|
|
+ $todaysCount++;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Add to recent recordings list
|
|
|
+ $recentRecordings[] = [
|
|
|
+ 'date' => $recordDate,
|
|
|
+ 'program' => $recording['Name'] ?? 'Unknown',
|
|
|
+ 'series' => $recording['SeriesName'] ?? '',
|
|
|
+ 'channel' => $recording['ChannelName'] ?? 'Unknown Channel',
|
|
|
+ 'status' => 'Completed'
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $stats['todaysRecordings'] = $todaysCount;
|
|
|
+ $stats['totalRecordings'] = $recordings['TotalRecordCount'] ?? count($allRecordings);
|
|
|
+ $stats['recentRecordings'] = array_slice($recentRecordings, 0, 10); // Limit to 10 recent recordings
|
|
|
+ }
|
|
|
+ } catch (Exception $e) {
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->warning('Failed to get recordings: ' . $e->getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ $this->setAPIResponse('success', 'LiveTV stats retrieved successfully', 200, $stats);
|
|
|
+ return true;
|
|
|
+
|
|
|
+ } catch (Exception $e) {
|
|
|
+ $this->setAPIResponse('error', 'Failed to retrieve LiveTV stats: ' . $e->getMessage(), 500);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function getHomepageEmbyLiveTVActivity()
|
|
|
+ {
|
|
|
+ $debugEnabled = $this->config['homepageEmbyLiveTVTrackerDebug'] ?? false;
|
|
|
+
|
|
|
+ if (!$this->homepageItemPermissions($this->embyLiveTVTrackerHomepagePermissions('main'), true)) {
|
|
|
+ if ($debugEnabled) {
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->warning('Permission denied for user');
|
|
|
+ }
|
|
|
+ $this->setAPIResponse('error', 'Permission denied', 403);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!$this->config['embyURL'] || !$this->config['embyToken']) {
|
|
|
+ $this->setAPIResponse('error', 'Emby URL or Token not configured', 500);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ if ($debugEnabled) {
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Activity method called - starting execution');
|
|
|
+ }
|
|
|
+ $options = $this->requestOptions($this->config['embyURL'], null, $this->config['embyDisableCertCheck'], $this->config['embyUseCustomCertificate']);
|
|
|
+ $baseUrl = $this->qualifyURL($this->config['embyURL']);
|
|
|
+ if ($debugEnabled) {
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Base URL configured: ' . $baseUrl);
|
|
|
+ }
|
|
|
+
|
|
|
+ $scheduledRecordings = [];
|
|
|
+ $completedRecordings = [];
|
|
|
+ $maxItems = intval($this->config['homepageEmbyLiveTVTrackerMaxItems'] ?? 10);
|
|
|
+ $showCompleted = $this->config['homepageEmbyLiveTVTrackerShowCompleted'] ?? true;
|
|
|
+ $maxCompletedItems = intval($this->config['homepageEmbyLiveTVTrackerMaxCompletedItems'] ?? 5);
|
|
|
+
|
|
|
+ // Get user info if user info is enabled
|
|
|
+ $userMap = [];
|
|
|
+ $showUserInfo = $this->config['homepageEmbyLiveTVTrackerShowUserInfo'] ?? false;
|
|
|
+ if ($showUserInfo) {
|
|
|
+ $usersUrl = $baseUrl . '/emby/Users?api_key=' . $this->config['embyToken'];
|
|
|
+ try {
|
|
|
+ $usersResponse = Requests::get($usersUrl, [], $options);
|
|
|
+ if ($usersResponse->success) {
|
|
|
+ $users = json_decode($usersResponse->body, true);
|
|
|
+ foreach ($users as $user) {
|
|
|
+ $userMap[$user['Id']] = $user['Name'];
|
|
|
+ }
|
|
|
+ if ($debugEnabled) {
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Retrieved ' . count($userMap) . ' users for mapping');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception $e) {
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->warning('Failed to get users: ' . $e->getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get scheduled recordings (active timers)
|
|
|
+ $timersUrl = $baseUrl . '/emby/LiveTv/Timers?api_key=' . $this->config['embyToken'] . '&Fields=ChannelName,ChannelId,SeriesName,ProgramInfo,StartDate,EndDate,UserId';
|
|
|
+ if ($debugEnabled) {
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Fetching timers from URL: ' . $timersUrl);
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ $timersResponse = Requests::get($timersUrl, [], $options);
|
|
|
+ if ($debugEnabled) {
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Timers API response status: ' . ($timersResponse->success ? 'success' : 'failed'));
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Timers API response code: ' . $timersResponse->status_code);
|
|
|
+ }
|
|
|
+ if ($timersResponse->success) {
|
|
|
+ $timers = json_decode($timersResponse->body, true);
|
|
|
+ $allTimers = $timers['Items'] ?? [];
|
|
|
+ if ($debugEnabled) {
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Retrieved ' . count($allTimers) . ' timers from Emby API');
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Timers API raw response length: ' . strlen($timersResponse->body));
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('First timer sample: ' . json_encode(array_slice($allTimers, 0, 1)));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Sort timers by start date
|
|
|
+ usort($allTimers, function($a, $b) {
|
|
|
+ $aDate = $a['StartDate'] ?? '';
|
|
|
+ $bDate = $b['StartDate'] ?? '';
|
|
|
+ return strcmp($aDate, $bDate);
|
|
|
+ });
|
|
|
+
|
|
|
+ $timersToProcess = array_slice($allTimers, 0, intval($maxItems));
|
|
|
+ if ($debugEnabled) {
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Processing ' . count($timersToProcess) . ' timers (maxItems: ' . $maxItems . ')');
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('All timers count before slice: ' . count($allTimers));
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('MaxItems value: ' . $maxItems . ' (type: ' . gettype($maxItems) . ')');
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach ($timersToProcess as $index => $timer) {
|
|
|
+ if ($debugEnabled) {
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Processing timer ' . ($index + 1) . ': ' . json_encode([
|
|
|
+ 'Name' => $timer['Name'] ?? 'no name',
|
|
|
+ 'StartDate' => $timer['StartDate'] ?? 'no start date',
|
|
|
+ 'EndDate' => $timer['EndDate'] ?? 'no end date',
|
|
|
+ 'ChannelName' => $timer['ChannelName'] ?? 'no channel name',
|
|
|
+ 'Status' => $timer['Status'] ?? 'no status',
|
|
|
+ 'UserId' => $timer['UserId'] ?? 'no user'
|
|
|
+ ]));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Calculate duration
|
|
|
+ $duration = '-';
|
|
|
+ if (isset($timer['StartDate']) && isset($timer['EndDate'])) {
|
|
|
+ $start = strtotime($timer['StartDate']);
|
|
|
+ $end = strtotime($timer['EndDate']);
|
|
|
+ if ($start && $end) {
|
|
|
+ $minutes = round(($end - $start) / 60);
|
|
|
+ $hours = floor($minutes / 60);
|
|
|
+ $remainingMinutes = $minutes % 60;
|
|
|
+ if ($hours > 0) {
|
|
|
+ $duration = sprintf('%dh %dm', $hours, $remainingMinutes);
|
|
|
+ } else {
|
|
|
+ $duration = sprintf('%dm', $minutes);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get channel name - timers should have this information
|
|
|
+ $channelName = $timer['ChannelName'] ?? null;
|
|
|
+ if (empty($channelName) && !empty($timer['ChannelId'])) {
|
|
|
+ $channelName = 'Channel ' . $timer['ChannelId'];
|
|
|
+ } elseif (empty($channelName)) {
|
|
|
+ $channelName = 'Unknown Channel';
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get user name
|
|
|
+ $userName = null;
|
|
|
+ if ($showUserInfo && !empty($timer['UserId']) && isset($userMap[$timer['UserId']])) {
|
|
|
+ $userName = $userMap[$timer['UserId']];
|
|
|
+ }
|
|
|
+
|
|
|
+ // Determine status based on timing
|
|
|
+ $status = 'Scheduled';
|
|
|
+ $startTime = strtotime($timer['StartDate'] ?? '');
|
|
|
+ $endTime = strtotime($timer['EndDate'] ?? '');
|
|
|
+ $now = time();
|
|
|
+
|
|
|
+ if ($startTime && $endTime) {
|
|
|
+ if ($now >= $startTime && $now <= $endTime) {
|
|
|
+ $status = 'Recording';
|
|
|
+ } elseif ($now > $endTime) {
|
|
|
+ $status = 'Completed';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get series name and episode title - try multiple approaches
|
|
|
+ $seriesName = '';
|
|
|
+ $episodeTitle = '';
|
|
|
+
|
|
|
+ // Check for episode title first
|
|
|
+ if (!empty($timer['ProgramInfo']['EpisodeTitle'])) {
|
|
|
+ $episodeTitle = $timer['ProgramInfo']['EpisodeTitle'];
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get series name
|
|
|
+ if (!empty($timer['SeriesName'])) {
|
|
|
+ $seriesName = $timer['SeriesName'];
|
|
|
+ } elseif (!empty($timer['ProgramInfo']['SeriesName'])) {
|
|
|
+ $seriesName = $timer['ProgramInfo']['SeriesName'];
|
|
|
+ } elseif (!empty($timer['ProgramInfo']['Name'])) {
|
|
|
+ $seriesName = $timer['ProgramInfo']['Name'];
|
|
|
+ } elseif (!empty($timer['Name'])) {
|
|
|
+ $seriesName = $timer['Name'];
|
|
|
+ }
|
|
|
+
|
|
|
+ // Use episode title if available, otherwise use program/series name
|
|
|
+ $displayName = $episodeTitle ? $episodeTitle : ($timer['Name'] ?? ($timer['ProgramInfo']['Name'] ?? 'Unknown Program'));
|
|
|
+
|
|
|
+ $activity = [
|
|
|
+ 'date' => $timer['StartDate'] ?? '',
|
|
|
+ 'name' => $displayName,
|
|
|
+ 'seriesName' => $seriesName,
|
|
|
+ 'episodeTitle' => $episodeTitle,
|
|
|
+ 'channelName' => $channelName,
|
|
|
+ 'userName' => $userName,
|
|
|
+ 'duration' => $duration,
|
|
|
+ 'status' => $status,
|
|
|
+ 'type' => 'timer'
|
|
|
+ ];
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Created activity for timer ' . ($index + 1) . ': ' . json_encode($activity));
|
|
|
+
|
|
|
+ // Debug timer date parsing
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Timer date debug - StartDate: "' . ($timer['StartDate'] ?? 'null') . '", parsed startTime: ' . ($startTime ? date('Y-m-d H:i:s', $startTime) : 'failed to parse') . ', EndDate: "' . ($timer['EndDate'] ?? 'null') . '", parsed endTime: ' . ($endTime ? date('Y-m-d H:i:s', $endTime) : 'failed to parse') . ', current time: ' . date('Y-m-d H:i:s', $now) . ', calculated status: ' . $status);
|
|
|
+
|
|
|
+ $scheduledRecordings[] = $activity;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception $e) {
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->warning('Failed to get timers for activity: ' . $e->getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get completed recordings if enabled
|
|
|
+ if ($showCompleted) {
|
|
|
+ $recordingsUrl = $baseUrl . '/emby/LiveTv/Recordings?api_key=' . $this->config['embyToken'] . '&StartIndex=0&Limit=' . $maxCompletedItems . '&Fields=DateCreated,SeriesName,RunTimeTicks&SortBy=DateCreated&SortOrder=Descending';
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Fetching completed recordings from URL: ' . $recordingsUrl);
|
|
|
+ try {
|
|
|
+ $recordingsResponse = Requests::get($recordingsUrl, [], $options);
|
|
|
+ if ($recordingsResponse->success) {
|
|
|
+ $recordings = json_decode($recordingsResponse->body, true);
|
|
|
+ $allRecordings = $recordings['Items'] ?? [];
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Retrieved ' . count($allRecordings) . ' completed recordings');
|
|
|
+
|
|
|
+ foreach ($allRecordings as $recording) {
|
|
|
+ if (isset($recording['DateCreated'])) {
|
|
|
+ // Format duration
|
|
|
+ $duration = '-';
|
|
|
+ if (isset($recording['RunTimeTicks']) && $recording['RunTimeTicks'] > 0) {
|
|
|
+ $minutes = floor($recording['RunTimeTicks'] / 600000000);
|
|
|
+ $hours = floor($minutes / 60);
|
|
|
+ $remainingMinutes = $minutes % 60;
|
|
|
+ if ($hours > 0) {
|
|
|
+ $duration = sprintf('%dh %dm', $hours, $remainingMinutes);
|
|
|
+ } else {
|
|
|
+ $duration = sprintf('%dm', $minutes);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $completedRecordings[] = [
|
|
|
+ 'date' => $recording['DateCreated'],
|
|
|
+ 'name' => $recording['Name'] ?? 'Unknown Program',
|
|
|
+ 'seriesName' => $recording['SeriesName'] ?? '',
|
|
|
+ 'channelName' => 'Unknown Channel', // Completed recordings don't have reliable channel info
|
|
|
+ 'userName' => null, // No user info available for completed recordings
|
|
|
+ 'duration' => $duration,
|
|
|
+ 'status' => 'Completed',
|
|
|
+ 'type' => 'recording'
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception $e) {
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->warning('Failed to get completed recordings: ' . $e->getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Final scheduled recordings count: ' . count($scheduledRecordings));
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Final completed recordings count: ' . count($completedRecordings));
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Sample scheduled: ' . json_encode(array_slice($scheduledRecordings, 0, 1)));
|
|
|
+ $this->setLoggerChannel('EmbyLiveTVTracker')->info('Sample completed: ' . json_encode(array_slice($completedRecordings, 0, 1)));
|
|
|
+
|
|
|
+ $this->setAPIResponse('success', 'LiveTV activity retrieved successfully', 200, [
|
|
|
+ 'scheduledRecordings' => $scheduledRecordings,
|
|
|
+ 'completedRecordings' => $completedRecordings
|
|
|
+ ]);
|
|
|
+ return true;
|
|
|
+
|
|
|
+ } catch (Exception $e) {
|
|
|
+ $this->setAPIResponse('error', 'Failed to retrieve LiveTV activity: ' . $e->getMessage(), 500);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+?>
|