ソースを参照

Merge pull request #1338 from henrywhitaker3/tautulli-homepage-item

Added Tautulli Homepage widget
causefx 6 年 前
コミット
0618474e8d

+ 18 - 2
api/config/default.php

@@ -107,7 +107,7 @@ return array(
 	'homepagCustomHTMLtwoAuth' => '1',
 	'homepageDelugeEnabled' => false,
 	'homepageDelugeAuth' => '1',
-    'homepageJdownloaderEnabled' => false,
+  'homepageJdownloaderEnabled' => false,
 	'homepageJdownloaderAuth' => '1',
 	'homepageSabnzbdEnabled' => false,
 	'homepageSabnzbdAuth' => '1',
@@ -162,7 +162,8 @@ return array(
 	'homepageOrderhealthchecks' => '17',
 	'homepageOrderjdownloader' => '18',
 	'homepageOrderunifi' => '19',
-	'homepageOrderPihole' => '20',
+  'homepageOrderPihole' => '20',
+	'homepageOrdertautulli' => '21',
 	'homepageShowStreamNames' => false,
 	'homepageShowStreamNamesAuth' => '1',
 	'homepageStreamRefresh' => '60000',
@@ -264,6 +265,21 @@ return array(
 	'performanceDisableIconDropdown' => false,
 	'performanceDisableImageDropdown' => false,
 	'traefikAuthEnable' => false,
+	'homepageTautulliEnabled' => false,
+	'homepageTautulliAuth' => '1',
+	'homepageTautulliLibraryAuth' => '1',
+	'homepageTautulliViewsAuth' => '1',
+	'homepageTautulliMiscAuth' => '1',
+	'homepageTautulliRefresh' => '60000',
+	'tautulliURL' => '',
+	'tautulliApikey' => '',
+	'tautulliLibraries' => true,
+	'tautulliTopMovies' => true,
+	'tautulliTopTV' => true,
+	'tautulliTopUsers' => true,
+	'tautulliTopPlatforms' => true,
+	'tautulliPopularMovies' => true,
+	'tautulliPopularTV' => true,
 	'homepagePiholeEnabled' => 'false',
 	'homepagePiholeAuth' => '1',
 	'homepagePiholeRefresh' => '10000',

+ 65 - 0
api/functions/homepage-connect-functions.php

@@ -60,6 +60,8 @@ function homepageConnect($array)
 		case 'getUnifi':
 			return unifiConnect();
 			break;
+		case 'getTautulli':
+			return getTautulli();
 		case 'getPihole':
 			return getPihole();
 			break;
@@ -2426,6 +2428,69 @@ function unifiConnect()
 	return false;
 }
 
+function getTautulli()
+{
+	if ($GLOBALS['homepageTautulliEnabled'] && !empty($GLOBALS['tautulliURL']) && !empty($GLOBALS['tautulliApikey']) && qualifyRequest($GLOBALS['homepageTautulliAuth'])) {
+		$api = [];
+		$url = $GLOBALS['tautulliURL'] . '/api/v2?apikey=' . $GLOBALS['tautulliApikey'];
+		try {
+			$homestatsUrl = $url . '&cmd=get_home_stats';
+			$homestats = Requests::get($homestatsUrl, [], []);
+			if ($homestats->success) {
+				$homestats = json_decode($homestats->body, true);
+				$api['homestats'] = $homestats['response'];
+			}
+			$libstatsUrl = $url . '&cmd=get_libraries';
+			$libstats = Requests::get($libstatsUrl, [], []);
+			if ($libstats->success) {
+				$libstats = json_decode($libstats->body, true);
+				$api['libstats'] = $libstats['response'];
+			}
+			$api['options'] = [
+				'url' => $GLOBALS['tautulliURL'],
+				'libraries' => $GLOBALS['tautulliLibraries'],
+				'topMovies' => $GLOBALS['tautulliTopMovies'],
+				'topTV' => $GLOBALS['tautulliTopTV'],
+				'topUsers' => $GLOBALS['tautulliTopUsers'],
+				'topPlatforms' => $GLOBALS['tautulliTopPlatforms'],
+				'popularMovies' => $GLOBALS['tautulliPopularMovies'],
+				'popularTV' => $GLOBALS['tautulliPopularTV'],
+			];
+
+			$ids = []; // Array of stat_ids to remove from the returned array
+			if(!qualifyRequest($GLOBALS['homepageTautulliLibraryAuth'])) {
+				$api['options']['libraries'] = false;
+				unset($api['libstats']);
+			}
+			if(!qualifyRequest($GLOBALS['homepageTautulliViewsAuth'])) {
+				$api['options']['topMovies'] = false;
+				$api['options']['topTV'] = false;
+				$api['options']['popularMovies'] = false;
+				$api['options']['popularTV'] = false;
+				$ids = array_merge([ 'top_movies', 'popular_movies', 'popular_tv', 'top_tv' ], $ids);
+				$api['homestats']['data'] = array_values($api['homestats']['data']);
+			}
+			if(!qualifyRequest($GLOBALS['homepageTautulliMiscAuth'])) {
+				$api['options']['topUsers'] = false;
+				$api['options']['topPlatforms'] = false;
+				$ids = array_merge([ 'top_platforms', 'top_users' ], $ids);
+				$api['homestats']['data'] = array_values($api['homestats']['data']);
+			}
+			$ids = array_merge([ 'top_music', 'popular_music', 'last_watched', 'most_concurrent' ], $ids);
+			foreach($ids as $id) {
+				$key = array_search($id, array_column($api['homestats']['data'], 'stat_id'));
+				unset($api['homestats']['data'][$key]);
+				$api['homestats']['data'] = array_values($api['homestats']['data']);
+			}
+		} catch (Requests_Exception $e) {
+			writeLog('error', 'Tautulli Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
+		};
+		$api = isset($api) ? $api : false;
+		return $api;
+	}
+	return false;
+}
+
 function testAPIConnection($array)
 {
 	switch ($array['data']['action']) {

+ 139 - 3
api/functions/homepage-functions.php

@@ -22,6 +22,7 @@ function homepageOrder()
 		"homepageOrderdownloader" => $GLOBALS['homepageOrderdownloader'],
 		"homepageOrderhealthchecks" => $GLOBALS['homepageOrderhealthchecks'],
 		"homepageOrderunifi" => $GLOBALS['homepageOrderunifi'],
+		"homepageOrdertautulli" => $GLOBALS['homepageOrdertautulli'],
 		"homepageOrderPihole" => $GLOBALS['homepageOrderPihole'],
 	);
 	asort($homepageOrder);
@@ -332,6 +333,18 @@ function buildHomepageItem($homepageItem)
 				';
 			}
 			break;
+		case 'homepageOrdertautulli':
+			if ($GLOBALS['homepageTautulliEnabled'] && qualifyRequest($GLOBALS['homepageTautulliAuth'])) {
+				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Tautulli...</h2></div>';
+				$item .= '
+				<script>
+				// Tautulli
+				homepageTautulli("' . $GLOBALS['homepageTautulliRefresh'] . '");
+				// End Tautulli
+				</script>
+				';
+			}
+			break;
 		case 'homepageOrderPihole':
 			if ($GLOBALS['homepagePiholeEnabled']) {
 				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Pi-hole Stats...</h2></div>';
@@ -2501,8 +2514,8 @@ function getHomepageList()
 				)
 			)
 		),
-		array(
-			'name' => 'Pi-hole',
+    array(
+      'name' => 'Pi-hole',
 			'enabled' => true,
 			'image' => 'plugins/images/tabs/pihole.png',
 			'category' => 'Monitor',
@@ -2542,7 +2555,123 @@ function getHomepageList()
 					),
 				),
 			)
-			),
+    ),
+		array(
+			'name' => 'Tautulli',
+			'enabled' => true,
+			'image' => 'plugins/images/tabs/tautulli.png',
+			'category' => 'Monitor',
+			'settings' => array(
+				'Enable' => array(
+					array(
+						'type' => 'switch',
+						'name' => 'homepageTautulliEnabled',
+						'label' => 'Enable',
+						'value' => $GLOBALS['homepageTautulliEnabled']
+					),
+					array(
+						'type' => 'select',
+						'name' => 'homepageTautulliAuth',
+						'label' => 'Minimum Authentication',
+						'value' => $GLOBALS['homepageTautulliAuth'],
+						'options' => $groups
+					)
+				),
+				'Connection' => array(
+					array(
+						'type' => 'input',
+						'name' => 'tautulliURL',
+						'label' => 'URL',
+						'value' => $GLOBALS['tautulliURL'],
+						'help' => 'URL for Tautulli API, include the IP, the port and the base URL (e.g. /tautulli/) in the URL',
+						'placeholder' => 'http://<ip>:<port>'
+					),
+					array(
+						'type' => 'password-alt',
+						'name' => 'tautulliApikey',
+						'label' => 'API Key',
+						'value' => $GLOBALS['tautulliApikey']
+					)
+				),
+				'Library Stats' => array(
+					array(
+						'type' => 'switch',
+						'name' => 'tautulliLibraries',
+						'label' => 'Libraries',
+						'value' => $GLOBALS['tautulliLibraries'],
+						'help' => 'Shows/hides the card with library information.',
+					),
+					array(
+						'type' => 'select',
+						'name' => 'homepageTautulliLibraryAuth',
+						'label' => 'Minimum Authentication',
+						'value' => $GLOBALS['homepageTautulliLibraryAuth'],
+						'options' => $groups
+					),
+				),
+				'Viewing Stats' => array(
+					array(
+						'type' => 'switch',
+						'name' => 'tautulliPopularMovies',
+						'label' => 'Popular Movies',
+						'value' => $GLOBALS['tautulliPopularMovies'],
+						'help' => 'Shows/hides the card with Popular Movies information.',
+					),
+					array(
+						'type' => 'switch',
+						'name' => 'tautulliPopularTV',
+						'label' => 'Popular TV',
+						'value' => $GLOBALS['tautulliPopularTV'],
+						'help' => 'Shows/hides the card with Popular TV information.',
+					),
+					array(
+						'type' => 'switch',
+						'name' => 'tautulliTopMovies',
+						'label' => 'Top Movies',
+						'value' => $GLOBALS['tautulliTopMovies'],
+						'help' => 'Shows/hides the card with Top Movies information.',
+					),
+					array(
+						'type' => 'switch',
+						'name' => 'tautulliTopTV',
+						'label' => 'Top TV',
+						'value' => $GLOBALS['tautulliTopTV'],
+						'help' => 'Shows/hides the card with Top TV information.',
+					),
+					array(
+						'type' => 'select',
+						'name' => 'homepageTautulliViewsAuth',
+						'label' => 'Minimum Authentication',
+						'value' => $GLOBALS['homepageTautulliViewsAuth'],
+						'options' => $groups
+					),
+				),
+				'Misc Stats' => array(
+					
+					array(
+						'type' => 'switch',
+						'name' => 'tautulliTopUsers',
+						'label' => 'Top Users',
+						'value' => $GLOBALS['tautulliTopUsers'],
+						'help' => 'Shows/hides the card with Top Users information.',
+					),
+					array(
+						'type' => 'switch',
+						'name' => 'tautulliTopPlatforms',
+						'label' => 'Top Platforms',
+						'value' => $GLOBALS['tautulliTopPlatforms'],
+						'help' => 'Shows/hides the card with Top Platforms information.',
+					),
+					array(
+						'type' => 'select',
+						'name' => 'homepageTautulliMiscAuth',
+						'label' => 'Minimum Authentication',
+						'value' => $GLOBALS['homepageTautulliMiscAuth'],
+						'options' => $groups
+					),
+				),
+			)
+		),
 	);
 }
 
@@ -2668,6 +2797,13 @@ function buildHomepageSettings()
 					$class .= ' faded';
 				}
 				break;
+			case 'homepageOrdertautulli':
+					$class = 'bg-info';
+					$image = 'plugins/images/tabs/tautulli.png';
+					if (!$GLOBALS['homepageTautulliEnabled']) {
+						$class .= ' faded';
+					}
+					break;
 			case 'homepageOrderPihole':
 				$class = 'bg-info';
 				$image = 'plugins/images/tabs/pihole.png';

+ 273 - 0
js/functions.js

@@ -6381,6 +6381,279 @@ function homepageCalendar(timeout){
 	if(typeof timeouts['calendar-Homepage'] !== 'undefined'){ clearTimeout(timeouts['calendar-Homepage']); }
 	timeouts['calendar-Homepage'] = setTimeout(function(){ homepageCalendar(timeout); }, timeout);
 }
+function buildTautulliItem(array){
+    var cards = `
+    <style>
+    .homepage-tautulli-card .poster {
+        max-width: 100%;
+        max-height: 15em;
+    }
+
+    .homepage-tautulli-card .lib-icon {
+        max-width: 100%;
+        height: 7.5em;
+    }
+
+    .homepage-tautulli-card .avatar {
+        border-radius: 50%;
+    }
+
+    .homepage-tautulli-card .align-self-center {
+        text-align: center;
+    }
+
+    .homepage-tautulli-card ol.pl-2 li p {
+        font-weight: 700;
+        font-size: 16px;
+    }
+
+    .one-line {
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+    }
+
+    .homepage-tautulli-card .bg-img-cont {
+        overflow: hidden;
+        pointer-events: none;
+        position: absolute;
+        width: 100%;
+        height: 100%;
+    }
+
+    .homepage-tautulli-card .bg-img {
+        width: 100%;
+        height: 100%;
+        top: 0;
+        left: 0;
+        filter: blur(7px) brightness(50%);
+    }
+
+    .lib-stats-row::before {
+        content: none !important;
+    }
+
+    .card-bg-colour {
+        background-color: #7b7b7b2e;
+    }
+
+    .platform-android-rgba { background-color: rgba(164, 202, 57, 0.40); }
+    .platform-atv-rgba { background-color: rgba(133, 132, 135, 0.40); }
+    .platform-chrome-rgba { background-color: rgba(237, 94, 80, 0.40); }
+    .platform-chromecast-rgba { background-color: rgba(16, 164, 232, 0.40); }
+    .platform-default-rgba { background-color: rgba(229, 160, 13, 0.40); }
+    .platform-dlna-rgba { background-color: rgba(12, 177, 75, 0.40); }
+    .platform-firefox-rgba { background-color: rgba(230, 120, 23, 0.40); }
+    .platform-gtv-rgba { background-color: rgba(0, 139, 207, 0.40); }
+    .platform-ie-rgba { background-color: rgba(0, 89, 158, 0.40); }
+    .platform-ios-rgba { background-color: rgba(133, 132, 135, 0.40); }
+    .platform-kodi-rgba { background-color: rgba(49, 175, 225, 0.40); }
+    .platform-linux-rgba { background-color: rgba(23, 147, 208, 0.40); }
+    .platform-macos-rgba { background-color: rgba(133, 132, 135, 0.40); }
+    .platform-msedge-rgba { background-color: rgba(0, 120, 215, 0.40); }
+    .platform-opera-rgba { background-color: rgba(255, 27, 45, 0.40); }
+    .platform-playstation-rgba { background-color: rgba(3, 77, 162, 0.40); }
+    .platform-plex-rgba { background-color: rgba(229, 160, 13, 0.40); }
+    .platform-plexamp-rgba { background-color: rgba(229, 160, 13, 0.40); }
+    .platform-roku-rgba { background-color: rgba(109, 60, 151, 0.40); }
+    .platform-safari-rgba { background-color: rgba(0, 169, 236, 0.40); }
+    .platform-samsung-rgba { background-color: rgba(3, 78, 162, 0.40); }
+    .platform-synclounge-rgba { background-color: rgba(21, 25, 36, 0.40); }
+    .platform-tivo-rgba { background-color: rgba(0, 167, 225, 0.40); }
+    .platform-wiiu-rgba { background-color: rgba(3, 169, 244, 0.40); }
+    .platform-windows-rgba { background-color: rgba(47, 192, 245, 0.40); }
+    .platform-wp-rgba { background-color: rgba(104, 33, 122, 0.40); }
+    .platform-xbmc-rgba { background-color: rgba(59, 72, 114, 0.40); }
+    .platform-xbox-rgba { background-color: rgba(16, 124, 16, 0.40); }
+    </style>
+    `;
+    var homestats = array.homestats.data;
+    var libstats = array.libstats;
+    var options = array.options;
+    var buildLibraries = function(data){
+        var libs = data.data;
+        var movies = [];
+        var tv = [];
+        var audio = [];
+
+        libs.forEach(e => {
+            switch(e['section_type']) {
+                case 'movie':
+                    movies.push(e);
+                    break;
+                case 'show':
+                    tv.push(e);
+                    break;
+                case 'artist':
+                    audio.push(e);
+                    break;
+                default:
+                    break;
+            }
+        });
+
+        movies = movies.sort((a, b) => (parseInt(a['count']) > parseInt(b['count'])) ? -1 : 1);
+        tv = tv.sort((a, b) => (parseInt(a['count']) > parseInt(b['count'])) ? -1 : 1);
+        audio = audio.sort((a, b) => (parseInt(a['count']) > parseInt(b['count'])) ? -1 : 1);
+
+        var buildCard = function(type, data) {
+            var card = `
+            <div class="col-lg-4 col-md-6 col-sm-12 col-xs-12">
+                <div class="card text-white mb-3 homepage-tautulli-card card-bg-colour">
+                    <div class="card-body">
+                        <div class="row" style="display: flex;">
+                            <div class="col-lg-4 col-md-4 col-sm-4 hidden-xs align-self-center">
+                                <img src="`+options['url']+`images/libraries/`+type;
+                                if(type == 'artist') {
+                                    card += `.png`;
+                                } else {
+                                    card += '.svg';
+                                }
+            card += `
+                                " class="lib-icon" alt="library icon">
+                            </div>
+                            <div class="col-lg-8 col-md-8 col-sm-8 col-xs-12">
+                                <ol class="pl-2">`;
+                                data.forEach(e => {
+                                    card += `<li class="w-100">
+                                                <p class="one-line d-inline">`+e['section_name']+`</p>`;
+                                                if(type == 'movie') {
+                                                    card += `<p class="mb-0 pull-right d-inline text-right text-warning">`+e['count']+`</p>`;
+                                                } else {
+                                                    card += `<p class="mb-0 pull-right d-inline text-right text-warning">`+e['count']+` / `+e['parent_count']+` / `+e['child_count']+`</p>`;
+                                                }
+                                    card += `
+                                            </li>`;
+                                });
+            card += `
+                                </ol>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            `;
+            return card;
+        };
+        var card = buildCard('movie', movies);
+        card += buildCard('show', tv);
+        card += buildCard('artist', audio);
+        return card;
+    };
+    var buildStats = function(data, stat){
+        var card = '';
+        data.forEach(e => {
+            if(e['stat_id'] == stat) {
+                if(stat === 'top_platforms') {
+                    classes = ' platform-' + e['rows'][0]['platform_name'] + '-rgba';
+                } else if(stat === 'top_users') {
+                    classes = ' card-bg-colour';
+                } else {
+                    classes = '';
+                }
+                card += `
+                <div class=col-lg-4 col-md-6 col-sm-12 col-xs-12">
+                    <div class="card text-white mb-3 homepage-tautulli-card`+classes+`">`;
+                        if(stat !== 'top_users' && stat !== 'top_platforms') {
+                            card += `
+                            <div class="bg-img-cont">
+                                <img class="bg-img" src="`+options['url']+`pms_image_proxy?img=`+e['rows'][0]['art']+`" alt="background art">
+                            </div>
+                            `;
+                        }
+                card += `
+                        <div class="card-body">
+                            <div class="row" style="display: flex;">
+                                <div class="col-lg-4 col-md-4 col-sm-4 hidden-xs align-self-center">`;
+                                if(stat == 'top_users') {
+                                    card += `<img src="`+e['rows'][0]['user_thumb']+`" class="poster avatar" alt="user avatar">`;
+                                } else if(stat == 'top_platforms') {
+                                    card += `<img src="`+options['url']+`images/platforms/`+e['rows'][0]['platform_name']+`.svg" class="poster" alt="platform icon">`;
+                                } else {
+                                    card += `<img src="`+options['url']+`pms_image_proxy?img=`+e['rows'][0]['thumb']+`" class="poster" alt="movie poster">`;
+                                }
+                card += `
+                                </div>
+                                <div class="col-lg-8 col-md-8 col-sm-8 col-xs-12">
+                                    <h4>`+e['stat_title']+`</h4>
+                                    <hr class="my-2">
+                                    <ol class="pl-2">`;
+                                    for(var i = 0; i < 5; i++) {
+                                        var item = e['rows'][i];
+                                        if(stat == 'top_users') {
+                                            card += `<li><p class="one-line">`+item['user']+`</p></li>`;
+                                        } else if(stat == 'top_platforms') {
+                                            card += `<li><p class="one-line">`+item['platform']+`</p></li>`;
+                                        } else {
+                                            card += `<li><p class="one-line">`+item['title']+`</p></li>`;
+                                        }
+                                    }
+                card += `
+                                    </ol>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                `;
+            } else {
+                return '';
+            }
+        });
+        return card;
+    };
+    cards += '<div class="row">'
+    cards += (options['popularMovies']) ? buildStats(homestats, 'popular_movies') : '';
+    cards += (options['popularTV']) ? buildStats(homestats, 'popular_tv') : '';
+    cards += (options['topMovies']) ? buildStats(homestats, 'top_movies') : '';
+    cards += (options['topTV']) ? buildStats(homestats, 'top_tv') : '';
+    cards += (options['topUsers']) ? buildStats(homestats, 'top_users') : '';
+    cards += (options['topPlatforms']) ? buildStats(homestats, 'top_platforms') : '';
+    cards += '</div>';
+    cards += '<div class="row">'
+    cards += (options['libraries']) ? buildLibraries(libstats) : '';
+    cards += '</div>';
+    return cards;
+}
+function buildTautulli(array){
+    if(array === false){ return ''; }
+    return (array) ? `
+    <div id="allPihole">
+		<div class="el-element-overlay row">
+		    <div class="col-md-12">
+		        <h4 class="pull-left homepage-element-title"><span lang="en">Tautulli</span></h4>
+		        <hr class="hidden-xs ml-2">
+		    </div>
+			<div class="clearfix"></div>
+            <div class="piholeCards col-sm-12">
+                `+buildTautulliItem(array)+`
+			</div>
+		</div>
+	</div>
+    ` : '';
+}
+function homepageTautulli(timeout){
+    var timeout = (typeof timeout !== 'undefined') ? timeout : activeInfo.settings.homepage.refresh.homepageTautulliRefresh;
+    organizrAPI('POST','api/?v1/homepage/connect',{action:'getTautulli'}).success(function(data) {
+        try {
+            var response = JSON.parse(data);
+        }catch(e) {
+            console.log(e + ' error: ' + data);
+            orgErrorAlert('<h4>' + e + '</h4>' + formatDebug(data));
+            return false;
+        }
+        document.getElementById('homepageOrdertautulli').innerHTML = '';
+        if(response.data !== null){
+            buildTautulli(response.data)
+            $('#homepageOrdertautulli').html(buildTautulli(response.data));
+        }
+    }).fail(function(xhr) {
+        console.error("Organizr Function: API Connection Failed");
+    });
+    var timeoutTitle = 'Tautulli-Homepage';
+    if(typeof timeouts[timeoutTitle] !== 'undefined'){ clearTimeout(timeouts[timeoutTitle]); }
+    timeouts[timeoutTitle] = setTimeout(function(){ homepageTautulli(timeout); }, timeout);
+}
 // Thanks Swifty!
 function PopupCenter(url, title, w, h) {
     // Fixes dual-screen position                         Most browsers      Firefox