| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- <?php
- /**
- * User Watch Statistics Homepage Plugin
- * Provides comprehensive user watching statistics from Plex/Emby/Jellyfin
- */
- trait HomepageUserWatchStats
- {
- /**
- * Get user watch statistics data
- */
- public function userWatchStatsHomepagePermissions($user)
- {
- // Check if user has access to statistics
- if ($user['groupID'] == 0 || $user['groupID'] == 1) {
- return true;
- }
- return false;
- }
- /**
- * Main function to get watch statistics
- */
- public function getUserWatchStats($options = null)
- {
- if (!$this->homepageItemPermissions($this->userWatchStatsHomepagePermissions($this->user), true)) {
- return false;
- }
- try {
- $mediaServer = $this->config['homepageUserWatchStatsService'] ?? 'plex';
- $days = intval($options['days'] ?? 30);
-
- switch (strtolower($mediaServer)) {
- case 'plex':
- return $this->getPlexWatchStats($days);
- case 'emby':
- return $this->getEmbyWatchStats($days);
- case 'jellyfin':
- return $this->getJellyfinWatchStats($days);
- default:
- return $this->getPlexWatchStats($days);
- }
- } catch (Exception $e) {
- $this->writeLog('error', 'User Watch Stats Error: ' . $e->getMessage(), 'SYSTEM');
- return [
- 'error' => true,
- 'message' => 'Failed to retrieve watch statistics'
- ];
- }
- }
- /**
- * Get Plex watch statistics via Tautulli API
- */
- private function getPlexWatchStats($days = 30)
- {
- $tautulliUrl = $this->config['plexURL'] ?? '';
- $tautulliToken = $this->config['plexToken'] ?? '';
-
- if (empty($tautulliUrl) || empty($tautulliToken)) {
- return ['error' => true, 'message' => 'Tautulli URL or API key not configured'];
- }
- $endDate = date('Y-m-d');
- $startDate = date('Y-m-d', strtotime("-{$days} days"));
-
- $stats = [
- 'period' => "{$days} days",
- 'start_date' => $startDate,
- 'end_date' => $endDate,
- 'most_watched' => $this->getTautulliMostWatched($tautulliUrl, $tautulliToken, $days),
- 'least_watched' => $this->getTautulliLeastWatched($tautulliUrl, $tautulliToken, $days),
- 'user_stats' => $this->getTautulliUserStats($tautulliUrl, $tautulliToken, $days),
- 'recent_activity' => $this->getTautulliRecentActivity($tautulliUrl, $tautulliToken),
- 'top_users' => $this->getTautulliTopUsers($tautulliUrl, $tautulliToken, $days)
- ];
- return $stats;
- }
- /**
- * Get most watched content from Tautulli
- */
- private function getTautulliMostWatched($url, $token, $days)
- {
- $endpoint = rtrim($url, '/') . '/api/v2';
- $params = [
- 'apikey' => $token,
- 'cmd' => 'get_home_stats',
- 'time_range' => $days,
- 'stats_type' => 'plays',
- 'stats_count' => 10
- ];
-
- $response = $this->guzzle->request('GET', $endpoint, [
- 'query' => $params,
- 'timeout' => 15,
- 'connect_timeout' => 15,
- 'http_errors' => false
- ]);
- if ($response->getStatusCode() === 200) {
- $data = json_decode($response->getBody(), true);
- return $data['response']['data'] ?? [];
- }
- return [];
- }
- /**
- * Get user statistics from Tautulli
- */
- private function getTautulliUserStats($url, $token, $days)
- {
- $endpoint = rtrim($url, '/') . '/api/v2';
- $params = [
- 'apikey' => $token,
- 'cmd' => 'get_user_watch_time_stats',
- 'time_range' => $days
- ];
-
- $response = $this->guzzle->request('GET', $endpoint, [
- 'query' => $params,
- 'timeout' => 15,
- 'connect_timeout' => 15,
- 'http_errors' => false
- ]);
- if ($response->getStatusCode() === 200) {
- $data = json_decode($response->getBody(), true);
- return $data['response']['data'] ?? [];
- }
- return [];
- }
- /**
- * Get top users from Tautulli
- */
- private function getTautulliTopUsers($url, $token, $days)
- {
- $endpoint = rtrim($url, '/') . '/api/v2';
- $params = [
- 'apikey' => $token,
- 'cmd' => 'get_users',
- 'length' => 25
- ];
-
- $response = $this->guzzle->request('GET', $endpoint, [
- 'query' => $params,
- 'timeout' => 15,
- 'connect_timeout' => 15,
- 'http_errors' => false
- ]);
- if ($response->getStatusCode() === 200) {
- $data = json_decode($response->getBody(), true);
- $users = $data['response']['data']['data'] ?? [];
-
- // Sort by play count
- usort($users, function($a, $b) {
- return ($b['play_count'] ?? 0) - ($a['play_count'] ?? 0);
- });
-
- return array_slice($users, 0, 10);
- }
- return [];
- }
- /**
- * Get recent activity from Tautulli
- */
- private function getTautulliRecentActivity($url, $token)
- {
- $endpoint = rtrim($url, '/') . '/api/v2';
- $params = [
- 'apikey' => $token,
- 'cmd' => 'get_recently_added',
- 'count' => 10
- ];
-
- $response = $this->guzzle->request('GET', $endpoint, [
- 'query' => $params,
- 'timeout' => 15,
- 'connect_timeout' => 15,
- 'http_errors' => false
- ]);
- if ($response->getStatusCode() === 200) {
- $data = json_decode($response->getBody(), true);
- return $data['response']['data']['recently_added'] ?? [];
- }
- return [];
- }
- /**
- * Get least watched content (inverse of most watched)
- */
- private function getTautulliLeastWatched($url, $token, $days)
- {
- $endpoint = rtrim($url, '/') . '/api/v2';
- $params = [
- 'apikey' => $token,
- 'cmd' => 'get_libraries',
- ];
-
- $response = $this->guzzle->request('GET', $endpoint, [
- 'query' => $params,
- 'timeout' => 15,
- 'connect_timeout' => 15,
- 'http_errors' => false
- ]);
- if ($response->getStatusCode() === 200) {
- $data = json_decode($response->getBody(), true);
- $libraries = $data['response']['data'] ?? [];
-
- $leastWatched = [];
- foreach ($libraries as $library) {
- $libraryStats = $this->getTautulliLibraryStats($url, $token, $library['section_id'], $days);
- if (!empty($libraryStats)) {
- $leastWatched = array_merge($leastWatched, array_slice($libraryStats, -10));
- }
- }
-
- return $leastWatched;
- }
- return [];
- }
- /**
- * Get library statistics for least watched calculation
- */
- private function getTautulliLibraryStats($url, $token, $sectionId, $days)
- {
- $endpoint = rtrim($url, '/') . '/api/v2';
- $params = [
- 'apikey' => $token,
- 'cmd' => 'get_library_media_info',
- 'section_id' => $sectionId,
- 'length' => 50,
- 'order_column' => 'play_count',
- 'order_dir' => 'asc'
- ];
-
- $response = $this->guzzle->request('GET', $endpoint, [
- 'query' => $params,
- 'timeout' => 15,
- 'connect_timeout' => 15,
- 'http_errors' => false
- ]);
- if ($response->getStatusCode() === 200) {
- $data = json_decode($response->getBody(), true);
- return $data['response']['data']['data'] ?? [];
- }
- return [];
- }
- /**
- * Get Emby watch statistics
- */
- private function getEmbyWatchStats($days = 30)
- {
- $embyUrl = $this->config['embyURL'] ?? '';
- $embyToken = $this->config['embyToken'] ?? '';
-
- if (empty($embyUrl) || empty($embyToken)) {
- return ['error' => true, 'message' => 'Emby URL or API key not configured'];
- }
- // Implement Emby-specific statistics gathering
- return $this->getGenericMediaServerStats('emby', $embyUrl, $embyToken, $days);
- }
- /**
- * Get Jellyfin watch statistics
- */
- private function getJellyfinWatchStats($days = 30)
- {
- $jellyfinUrl = $this->config['jellyfinURL'] ?? '';
- $jellyfinToken = $this->config['jellyfinToken'] ?? '';
-
- if (empty($jellyfinUrl) || empty($jellyfinToken)) {
- return ['error' => true, 'message' => 'Jellyfin URL or API key not configured'];
- }
- // Implement Jellyfin-specific statistics gathering
- return $this->getGenericMediaServerStats('jellyfin', $jellyfinUrl, $jellyfinToken, $days);
- }
- /**
- * Generic media server stats for Emby/Jellyfin
- */
- private function getGenericMediaServerStats($type, $url, $token, $days)
- {
- // Basic structure for now - can be expanded based on Emby/Jellyfin APIs
- return [
- 'period' => "{$days} days",
- 'start_date' => date('Y-m-d', strtotime("-{$days} days")),
- 'end_date' => date('Y-m-d'),
- 'message' => ucfirst($type) . ' statistics coming soon',
- 'most_watched' => [],
- 'least_watched' => [],
- 'user_stats' => [],
- 'recent_activity' => [],
- 'top_users' => []
- ];
- }
- /**
- * Format duration for display
- */
- private function formatDuration($seconds)
- {
- if ($seconds < 3600) {
- return gmdate('i:s', $seconds);
- } else {
- return gmdate('H:i:s', $seconds);
- }
- }
- /**
- * Get user avatar URL
- */
- private function getUserAvatar($userId, $mediaServer = 'plex')
- {
- switch ($mediaServer) {
- case 'plex':
- return $this->getPlexUserAvatar($userId);
- case 'emby':
- return $this->getEmbyUserAvatar($userId);
- case 'jellyfin':
- return $this->getJellyfinUserAvatar($userId);
- default:
- return '/plugins/images/organizr/user-bg.png';
- }
- }
- /**
- * Get Plex user avatar
- */
- private function getPlexUserAvatar($userId)
- {
- $tautulliUrl = $this->config['plexURL'] ?? '';
- $tautulliToken = $this->config['plexToken'] ?? '';
-
- if (empty($tautulliUrl) || empty($tautulliToken)) {
- return '/plugins/images/organizr/user-bg.png';
- }
- $endpoint = rtrim($tautulliUrl, '/') . "/api/v2";
- $params = [
- 'apikey' => $tautulliToken,
- 'cmd' => 'get_user_thumb',
- 'user_id' => $userId
- ];
- try {
- $response = $this->guzzle->request('GET', $endpoint, [
- 'query' => $params,
- 'timeout' => 10,
- 'connect_timeout' => 10,
- 'http_errors' => false
- ]);
- if ($response->getStatusCode() === 200) {
- $data = json_decode($response->getBody(), true);
- return $data['response']['data']['thumb'] ?? '/plugins/images/organizr/user-bg.png';
- }
- } catch (Exception $e) {
- // Return default avatar on error
- }
- return '/plugins/images/organizr/user-bg.png';
- }
- /**
- * Get Emby user avatar
- */
- private function getEmbyUserAvatar($userId)
- {
- // Implement Emby avatar logic
- return '/plugins/images/organizr/user-bg.png';
- }
- /**
- * Get Jellyfin user avatar
- */
- private function getJellyfinUserAvatar($userId)
- {
- // Implement Jellyfin avatar logic
- return '/plugins/images/organizr/user-bg.png';
- }
- }
|