Browse Source

Merge pull request #2 from causefx/v2-develop

V2 develop
Henry Whitaker 6 years ago
parent
commit
b371e3d623

+ 4 - 0
.gitignore

@@ -151,3 +151,7 @@ api/plugins/*
 # =========================
 api/pages/custom/*.php
 /plugins/images/tabs/eatsleep.jpg
+/plugins/images/cache/tautulli-show.svg
+/plugins/images/cache/tautulli-android.svg
+/plugins/images/cache/tautulli-artist.png
+/plugins/images/cache/tautulli-movie.svg

+ 3 - 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,7 @@ return array(
 	'homepageOrderhealthchecks' => '17',
 	'homepageOrderjdownloader' => '18',
 	'homepageOrderunifi' => '19',
-  'homepageOrderPihole' => '20',
+	'homepageOrderPihole' => '20',
 	'homepageOrdertautulli' => '21',
 	'homepageShowStreamNames' => false,
 	'homepageShowStreamNamesAuth' => '1',
@@ -279,6 +279,7 @@ return array(
 	'tautulliTopPlatforms' => true,
 	'tautulliPopularMovies' => true,
 	'tautulliPopularTV' => true,
+	'tautulliHeader' => 'Tautulli',
 	'homepagePiholeEnabled' => false,
 	'homepagePiholeAuth' => '1',
 	'homepagePiholeRefresh' => '10000',

+ 98 - 102
api/functions/homepage-connect-functions.php

@@ -123,7 +123,7 @@ function getPihole()
 	if ($GLOBALS['homepagePiholeEnabled'] && !empty($GLOBALS['piholeURL'])) {
 		$api = array();
 		$urls = explode(',', $GLOBALS['piholeURL']);
-		foreach($urls as $url) {
+		foreach ($urls as $url) {
 			$url = $url . '/api.php?';
 			try {
 				$response = Requests::get($url, [], []);
@@ -600,11 +600,11 @@ function plexConnect($action, $key = null)
 				$resolve = false;
 				break;
 			case 'recent':
-					//$url = $url . "/library/recentlyAdded?X-Plex-Token=" . $GLOBALS['plexToken'] . "&limit=" . $GLOBALS['homepageRecentLimit'];
-					$urls['movie'] = $url . "/hubs/home/recentlyAdded?X-Plex-Token=" . $GLOBALS['plexToken'] . "&X-Plex-Container-Start=0&X-Plex-Container-Size=" . $GLOBALS['homepageRecentLimit'] . "&type=1";
-					$urls['tv'] = $url . "/hubs/home/recentlyAdded?X-Plex-Token=" . $GLOBALS['plexToken'] . "&X-Plex-Container-Start=0&X-Plex-Container-Size=" . $GLOBALS['homepageRecentLimit'] . "&type=2";
-					$urls['music'] = $url . "/hubs/home/recentlyAdded?X-Plex-Token=" . $GLOBALS['plexToken'] . "&X-Plex-Container-Start=0&X-Plex-Container-Size=" . $GLOBALS['homepageRecentLimit'] . "&type=8";
-					$multipleURL = true;
+				//$url = $url . "/library/recentlyAdded?X-Plex-Token=" . $GLOBALS['plexToken'] . "&limit=" . $GLOBALS['homepageRecentLimit'];
+				$urls['movie'] = $url . "/hubs/home/recentlyAdded?X-Plex-Token=" . $GLOBALS['plexToken'] . "&X-Plex-Container-Start=0&X-Plex-Container-Size=" . $GLOBALS['homepageRecentLimit'] . "&type=1";
+				$urls['tv'] = $url . "/hubs/home/recentlyAdded?X-Plex-Token=" . $GLOBALS['plexToken'] . "&X-Plex-Container-Start=0&X-Plex-Container-Size=" . $GLOBALS['homepageRecentLimit'] . "&type=2";
+				$urls['music'] = $url . "/hubs/home/recentlyAdded?X-Plex-Token=" . $GLOBALS['plexToken'] . "&X-Plex-Container-Start=0&X-Plex-Container-Size=" . $GLOBALS['homepageRecentLimit'] . "&type=8";
+				$multipleURL = true;
 				break;
 			case 'metadata':
 				$url = $url . "/library/metadata/" . $key . "?X-Plex-Token=" . $GLOBALS['plexToken'];
@@ -765,18 +765,17 @@ function embyConnect($action, $key = 'Latest', $skip = false)
 			if ($response->success) {
 				$items = array();
 				$emby = json_decode($response->body, true);
-				if($key !== 'Latest'){
+				if ($key !== 'Latest') {
 					if (isset($emby['NowPlayingItem']) || isset($emby['Name'])) {
 						$items[] = resolveEmbyItem($emby);
 					}
-				}else{
+				} else {
 					foreach ($emby as $child) {
 						if (isset($child['NowPlayingItem']) || isset($child['Name'])) {
 							$items[] = resolveEmbyItem($child);
 						}
 					}
 				}
-				
 				$api['content'] = array_filter($items);
 				return $api;
 			}
@@ -789,45 +788,43 @@ function embyConnect($action, $key = 'Latest', $skip = false)
 
 function jdownloaderConnect()
 {
-    if ($GLOBALS['homepageJdownloaderEnabled'] && !empty($GLOBALS['jdownloaderURL']) && qualifyRequest($GLOBALS['homepageJdownloaderAuth'])) {
-        $url = qualifyURL($GLOBALS['jdownloaderURL']);
-
-        try {
-            $options = (localURL($url)) ? array('verify' => false, 'timeout' => 30) : array('timeout' => 30);
-            $response = Requests::get($url, array(), $options);
-            if ($response->success) {
-                $temp = json_decode($response->body, true);
-                $packages = $temp['packages'];
-                if ($packages['downloader']) {
-                    $api['content']['queueItems'] = $packages['downloader'];
-                }else{
-                    $api['content']['queueItems'] = [];
-                }
-                if ($packages['linkgrabber_decrypted']) {
-                    $api['content']['grabberItems'] = $packages['linkgrabber_decrypted'];
-                }else{
-                    $api['content']['grabberItems'] = [];
-                }
-                if ($packages['linkgrabber_failed']) {
-                    $api['content']['encryptedItems'] = $packages['linkgrabber_failed'];
-                }else{
-                    $api['content']['encryptedItems'] = [];
-                }
-                if ($packages['linkgrabber_offline']) {
-                    $api['content']['offlineItems'] = $packages['linkgrabber_offline'];
-                }else{
-                    $api['content']['offlineItems'] = [];
-                }
-
-                $api['content']['$status'] = array($temp['downloader_state'], $temp['grabber_collecting'], $temp['update_ready']);
-            }
-        } catch (Requests_Exception $e) {
-            writeLog('error', 'JDownloader Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-        };
-        $api['content'] = isset($api['content']) ? $api['content'] : false;
-        return $api;
-    }
-    return false;
+	if ($GLOBALS['homepageJdownloaderEnabled'] && !empty($GLOBALS['jdownloaderURL']) && qualifyRequest($GLOBALS['homepageJdownloaderAuth'])) {
+		$url = qualifyURL($GLOBALS['jdownloaderURL']);
+		try {
+			$options = (localURL($url)) ? array('verify' => false, 'timeout' => 30) : array('timeout' => 30);
+			$response = Requests::get($url, array(), $options);
+			if ($response->success) {
+				$temp = json_decode($response->body, true);
+				$packages = $temp['packages'];
+				if ($packages['downloader']) {
+					$api['content']['queueItems'] = $packages['downloader'];
+				} else {
+					$api['content']['queueItems'] = [];
+				}
+				if ($packages['linkgrabber_decrypted']) {
+					$api['content']['grabberItems'] = $packages['linkgrabber_decrypted'];
+				} else {
+					$api['content']['grabberItems'] = [];
+				}
+				if ($packages['linkgrabber_failed']) {
+					$api['content']['encryptedItems'] = $packages['linkgrabber_failed'];
+				} else {
+					$api['content']['encryptedItems'] = [];
+				}
+				if ($packages['linkgrabber_offline']) {
+					$api['content']['offlineItems'] = $packages['linkgrabber_offline'];
+				} else {
+					$api['content']['offlineItems'] = [];
+				}
+				$api['content']['$status'] = array($temp['downloader_state'], $temp['grabber_collecting'], $temp['update_ready']);
+			}
+		} catch (Requests_Exception $e) {
+			writeLog('error', 'JDownloader Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
+		};
+		$api['content'] = isset($api['content']) ? $api['content'] : false;
+		return $api;
+	}
+	return false;
 }
 
 function sabnzbdConnect()
@@ -868,7 +865,7 @@ function nzbgetConnect()
 		$url = $url . '/jsonrpc/listgroups';
 		try {
 			$options = (localURL($url)) ? array('verify' => false) : array();
-			if($GLOBALS['nzbgetUsername'] !== '' && decrypt($GLOBALS['nzbgetPassword']) !== ''){
+			if ($GLOBALS['nzbgetUsername'] !== '' && decrypt($GLOBALS['nzbgetPassword']) !== '') {
 				$credentials = array('auth' => new Requests_Auth_Basic(array($GLOBALS['nzbgetUsername'], decrypt($GLOBALS['nzbgetPassword']))));
 				$options = array_merge($options, $credentials);
 			}
@@ -883,7 +880,7 @@ function nzbgetConnect()
 		$url = $url . '/jsonrpc/history';
 		try {
 			$options = (localURL($url)) ? array('verify' => false) : array();
-			if($GLOBALS['nzbgetUsername'] !== '' && decrypt($GLOBALS['nzbgetPassword']) !== ''){
+			if ($GLOBALS['nzbgetUsername'] !== '' && decrypt($GLOBALS['nzbgetPassword']) !== '') {
 				$credentials = array('auth' => new Requests_Auth_Basic(array($GLOBALS['nzbgetUsername'], decrypt($GLOBALS['nzbgetPassword']))));
 				$options = array_merge($options, $credentials);
 			}
@@ -983,7 +980,7 @@ function rTorrentConnect()
 			$extraPath = (empty($GLOBALS['rTorrentURLOverride'])) ? $extraPath : '';
 			$url = $digest['scheme'] . '://' . $passwordInclude . $digest['host'] . $digest['port'] . $digest['path'] . $extraPath;
 			$options = (localURL($url, $GLOBALS['rTorrentDisableCertCheck'])) ? array('verify' => false) : array();
-			if($GLOBALS['rTorrentUsername'] !== '' && decrypt($GLOBALS['rTorrentPassword']) !== ''){
+			if ($GLOBALS['rTorrentUsername'] !== '' && decrypt($GLOBALS['rTorrentPassword']) !== '') {
 				$credentials = array('auth' => new Requests_Auth_Digest(array($GLOBALS['rTorrentUsername'], decrypt($GLOBALS['rTorrentPassword']))));
 				$options = array_merge($options, $credentials);
 			}
@@ -1494,10 +1491,10 @@ function getCalenderRepeatUntil($value)
 {
 	$first = explode('UNTIL=', $value);
 	if (count($first) > 1) {
-		if(strpos($first[1], ';') !== false){
+		if (strpos($first[1], ';') !== false) {
 			$check = explode(';', $first[1]);
 			return $check[0];
-		}else{
+		} else {
 			return $first[1];
 		}
 	} else {
@@ -1725,7 +1722,7 @@ function getRadarrCalendar($array, $number, $url)
 							$imageUrl = implode("/", $imageParts);
 						}
 						$banner = $url . $imageUrl . '?apikey=' . $GLOBALS['radarrToken'];
-					}else{
+					} else {
 						$banner = $image['url'];
 					}
 					
@@ -2409,7 +2406,7 @@ function unifiConnect()
 			if ($response->success) {
 				$cookie['unifises'] = ($response->cookies['unifises']->value) ?? false;
 				$cookie['csrf_token'] = ($response->cookies['csrf_token']->value) ?? false;
-			}else{
+			} else {
 				return false;
 			}
 			$headers = array(
@@ -2433,42 +2430,42 @@ function getTautulli()
 	if ($GLOBALS['homepageTautulliEnabled'] && !empty($GLOBALS['tautulliURL']) && !empty($GLOBALS['tautulliApikey']) && qualifyRequest($GLOBALS['homepageTautulliAuth'])) {
 		$api = [];
 		$url = qualifyURL($GLOBALS['tautulliURL']);
-		$url = $url . '/api/v2?apikey=' . $GLOBALS['tautulliApikey'];
+		$apiURL = $url . '/api/v2?apikey=' . $GLOBALS['tautulliApikey'];
 		try {
-			$homestatsUrl = $url . '&cmd=get_home_stats';
+			$homestatsUrl = $apiURL . '&cmd=get_home_stats';
 			$homestats = Requests::get($homestatsUrl, [], []);
 			if ($homestats->success) {
 				$homestats = json_decode($homestats->body, true);
 				$api['homestats'] = $homestats['response'];
 				// Cache art & thumb for first result in each tautulli API result
-				$categories = [ 'top_movies', 'top_tv', 'popular_movies', 'popular_tv' ];
-				foreach($categories as $cat) {
+				$categories = ['top_movies', 'top_tv', 'popular_movies', 'popular_tv'];
+				foreach ($categories as $cat) {
 					$key = array_search($cat, array_column($api['homestats']['data'], 'stat_id'));
 					$img = $api['homestats']['data'][$key]['rows'][0];
-					cacheImage($GLOBALS['tautulliURL'] . 'pms_image_proxy?img=' . $img['art'], $img['title'] . '-art');
-					cacheImage($GLOBALS['tautulliURL'] . 'pms_image_proxy?img=' . $img['thumb'], $img['title'] . '-thumb');
-					$img['art'] = '/plugins/images/cache/' . $img['title'] . '-art.jpg';
-					$img['thumb'] = '/plugins/images/cache/' . $img['title'] . '-thumb.jpg';
+					cacheImage($url . '/pms_image_proxy?img=' . $img['art'], $img['rating_key'] . '-np');
+					cacheImage($url . '/pms_image_proxy?img=' . $img['thumb'], $img['rating_key'] . '-list');
+					$img['art'] = 'plugins/images/cache/' . $img['rating_key'] . '-np.jpg';
+					$img['thumb'] = 'plugins/images/cache/' . $img['rating_key'] . '-list.jpg';
 					$api['homestats']['data'][$key]['rows'][0] = $img;
 				}
 				// Cache the platform icon
 				$key = array_search('top_platforms', array_column($api['homestats']['data'], 'stat_id'));
 				$platform = $api['homestats']['data'][$key]['rows'][0]['platform_name'];
-				cacheImage($GLOBALS['tautulliURL'] . 'images/platforms/' . $platform . '.svg', 'tautulli-' . $platform, 'svg');
+				cacheImage($url . '/images/platforms/' . $platform . '.svg', 'tautulli-' . $platform, 'svg');
 			}
-			$libstatsUrl = $url . '&cmd=get_libraries';
+			$libstatsUrl = $apiURL . '&cmd=get_libraries';
 			$libstats = Requests::get($libstatsUrl, [], []);
 			if ($libstats->success) {
 				$libstats = json_decode($libstats->body, true);
 				$api['libstats'] = $libstats['response'];
-				$categories = [ 'movie.svg', 'show.svg', 'artist.png' ];
-				foreach($categories as $cat) {
+				$categories = ['movie.svg', 'show.svg', 'artist.png'];
+				foreach ($categories as $cat) {
 					$parts = explode('.', $cat);
-					cacheImage($GLOBALS['tautulliURL'] . 'images/libraries/' . $cat, 'tautulli-' . $parts[0], $parts[1]);
+					cacheImage($url . '/images/libraries/' . $cat, 'tautulli-' . $parts[0], $parts[1]);
 				}
 			}
 			$api['options'] = [
-				'url' => $GLOBALS['tautulliURL'],
+				'url' => $url,
 				'libraries' => $GLOBALS['tautulliLibraries'],
 				'topMovies' => $GLOBALS['tautulliTopMovies'],
 				'topTV' => $GLOBALS['tautulliTopTV'],
@@ -2477,28 +2474,27 @@ function getTautulli()
 				'popularMovies' => $GLOBALS['tautulliPopularMovies'],
 				'popularTV' => $GLOBALS['tautulliPopularTV'],
 			];
-
 			$ids = []; // Array of stat_ids to remove from the returned array
-			if(!qualifyRequest($GLOBALS['homepageTautulliLibraryAuth'])) {
+			if (!qualifyRequest($GLOBALS['homepageTautulliLibraryAuth'])) {
 				$api['options']['libraries'] = false;
 				unset($api['libstats']);
 			}
-			if(!qualifyRequest($GLOBALS['homepageTautulliViewsAuth'])) {
+			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);
+				$ids = array_merge(['top_movies', 'popular_movies', 'popular_tv', 'top_tv'], $ids);
 				$api['homestats']['data'] = array_values($api['homestats']['data']);
 			}
-			if(!qualifyRequest($GLOBALS['homepageTautulliMiscAuth'])) {
+			if (!qualifyRequest($GLOBALS['homepageTautulliMiscAuth'])) {
 				$api['options']['topUsers'] = false;
 				$api['options']['topPlatforms'] = false;
-				$ids = array_merge([ 'top_platforms', 'top_users' ], $ids);
+				$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) {
+			$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']);
@@ -2516,7 +2512,7 @@ function testAPIConnection($array)
 {
 	switch ($array['data']['action']) {
 		case 'unifiSite':
-			if (!empty($GLOBALS['unifiURL']) && !empty($GLOBALS['unifiUsername'])  && !empty($GLOBALS['unifiPassword'])) {
+			if (!empty($GLOBALS['unifiURL']) && !empty($GLOBALS['unifiUsername']) && !empty($GLOBALS['unifiPassword'])) {
 				$url = qualifyURL($GLOBALS['unifiURL']);
 				try {
 					$options = array('verify' => false, 'verifyname' => false, 'follow_redirects' => false);
@@ -2530,7 +2526,7 @@ function testAPIConnection($array)
 					if ($response->success) {
 						$cookie['unifises'] = ($response->cookies['unifises']->value) ?? false;
 						$cookie['csrf_token'] = ($response->cookies['csrf_token']->value) ?? false;
-					}else{
+					} else {
 						return false;
 					}
 					$headers = array(
@@ -2540,7 +2536,7 @@ function testAPIConnection($array)
 					if ($response->success) {
 						$body = json_decode($response->body, true);
 						return $body;
-					}else{
+					} else {
 						return false;
 					}
 				} catch (Requests_Exception $e) {
@@ -2549,7 +2545,7 @@ function testAPIConnection($array)
 			}
 			break;
 		case 'unifi':
-			if (!empty($GLOBALS['unifiURL']) && !empty($GLOBALS['unifiUsername'])  && !empty($GLOBALS['unifiPassword']) && !empty($GLOBALS['unifiSiteName'])) {
+			if (!empty($GLOBALS['unifiURL']) && !empty($GLOBALS['unifiUsername']) && !empty($GLOBALS['unifiPassword']) && !empty($GLOBALS['unifiSiteName'])) {
 				$url = qualifyURL($GLOBALS['unifiURL']);
 				try {
 					$options = array('verify' => false, 'verifyname' => false, 'follow_redirects' => false);
@@ -2563,19 +2559,19 @@ function testAPIConnection($array)
 					if ($response->success) {
 						$cookie['unifises'] = ($response->cookies['unifises']->value) ?? false;
 						$cookie['csrf_token'] = ($response->cookies['csrf_token']->value) ?? false;
-					}else{
+					} else {
 						return 'Failed to Login';
 					}
 					$headers = array(
 						'cookie' => 'unifises=' . $cookie['unifises'] . ';' . 'csrf_token=' . $cookie['csrf_token'] . ';'
 					);
-					$response = Requests::get($url . '/api/s/'.$GLOBALS['unifiSiteName'].'/self', $headers, $options);
+					$response = Requests::get($url . '/api/s/' . $GLOBALS['unifiSiteName'] . '/self', $headers, $options);
 					$body = json_decode($response->body, true);
 					return ($body['meta']['rc'] == 'ok') ? true : $body['meta']['msg'];
 				} catch (Requests_Exception $e) {
 					writeLog('error', 'Unifi Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
 				};
-			}else{
+			} else {
 				return 'Not all data is filled in...';
 			}
 			break;
@@ -2701,22 +2697,22 @@ function testAPIConnection($array)
 				return 'URL/s and/or Token/s not setup';
 			}
 			break;
-        case 'jdownloader':
-            if (!empty($GLOBALS['jdownloaderURL'])) {
-                $url = qualifyURL($GLOBALS['jdownloaderURL']);
-                try {
-	                $options = (localURL($url)) ? array('verify' => false, 'timeout' => 30) : array('timeout' => 30);
-                    $response = Requests::get($url, array(), $options);
-                    if ($response->success) {
-                        return true;
-                    }
-                } catch (Requests_Exception $e) {
-                    return $e->getMessage();
-                };
-            } else {
-                return 'URL and/or Token not setup';
-            }
-            break;
+		case 'jdownloader':
+			if (!empty($GLOBALS['jdownloaderURL'])) {
+				$url = qualifyURL($GLOBALS['jdownloaderURL']);
+				try {
+					$options = (localURL($url)) ? array('verify' => false, 'timeout' => 30) : array('timeout' => 30);
+					$response = Requests::get($url, array(), $options);
+					if ($response->success) {
+						return true;
+					}
+				} catch (Requests_Exception $e) {
+					return $e->getMessage();
+				};
+			} else {
+				return 'URL and/or Token not setup';
+			}
+			break;
 		case 'sabnzbd':
 			if (!empty($GLOBALS['sabnzbdURL']) && !empty($GLOBALS['sabnzbdToken'])) {
 				$url = qualifyURL($GLOBALS['sabnzbdURL']);
@@ -2740,7 +2736,7 @@ function testAPIConnection($array)
 				$url = $url . '/jsonrpc/listgroups';
 				try {
 					$options = (localURL($url)) ? array('verify' => false) : array();
-					if($GLOBALS['nzbgetUsername'] !== '' && decrypt($GLOBALS['nzbgetPassword']) !== ''){
+					if ($GLOBALS['nzbgetUsername'] !== '' && decrypt($GLOBALS['nzbgetPassword']) !== '') {
 						$credentials = array('auth' => new Requests_Auth_Basic(array($GLOBALS['nzbgetUsername'], decrypt($GLOBALS['nzbgetPassword']))));
 						$options = array_merge($options, $credentials);
 					}
@@ -2777,7 +2773,7 @@ function testAPIConnection($array)
 					$extraPath = (empty($GLOBALS['rTorrentURLOverride'])) ? $extraPath : '';
 					$url = $digest['scheme'] . '://' . $passwordInclude . $digest['host'] . $digest['port'] . $digest['path'] . $extraPath;
 					$options = (localURL($url, $GLOBALS['rTorrentDisableCertCheck'])) ? array('verify' => false) : array();
-					if($GLOBALS['rTorrentUsername'] !== '' && decrypt($GLOBALS['rTorrentPassword']) !== ''){
+					if ($GLOBALS['rTorrentUsername'] !== '' && decrypt($GLOBALS['rTorrentPassword']) !== '') {
 						$credentials = array('auth' => new Requests_Auth_Digest(array($GLOBALS['rTorrentUsername'], decrypt($GLOBALS['rTorrentPassword']))));
 						$options = array_merge($options, $credentials);
 					}

+ 95 - 87
api/functions/homepage-functions.php

@@ -177,10 +177,10 @@ function buildHomepageItem($homepageItem)
 				}
 			}
 			break;
-        case 'homepageOrderjdownloader':
-            if ($GLOBALS['homepageJdownloaderEnabled'] && qualifyRequest($GLOBALS['homepageJdownloaderAuth'])) {
-                if ($GLOBALS['jdownloaderCombine']) {
-                    $item .= '
+		case 'homepageOrderjdownloader':
+			if ($GLOBALS['homepageJdownloaderEnabled'] && qualifyRequest($GLOBALS['homepageJdownloaderAuth'])) {
+				if ($GLOBALS['jdownloaderCombine']) {
+					$item .= '
 					<script>
 					// JDownloader
 					buildDownloaderCombined(\'jdownloader\');
@@ -188,9 +188,9 @@ function buildHomepageItem($homepageItem)
 					// End JDownloader
 					</script>
 					';
-                } else {
-                    $item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
-                    $item .= '
+				} else {
+					$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
+					$item .= '
 					<script>
 					// JDownloader
 					$("#' . $homepageItem . '").html(buildDownloader("jdownloader"));
@@ -198,9 +198,9 @@ function buildHomepageItem($homepageItem)
 					// End JDownloader
 					</script>
 					';
-                }
-            }
-            break;
+				}
+			}
+			break;
 		case 'homepageOrdersabnzbd':
 			if ($GLOBALS['homepageSabnzbdEnabled'] && qualifyRequest($GLOBALS['homepageSabnzbdAuth'])) {
 				if ($GLOBALS['sabnzbdCombine']) {
@@ -1048,13 +1048,13 @@ function getHomepageList()
 				)
 			)
 		),
-        array(
-            'name' => 'JDownloader',
-            'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-            'image' => 'plugins/images/tabs/jdownloader.png',
-            'category' => 'Downloader',
-            'settings' => array(
-	            'custom' => '
+		array(
+			'name' => 'JDownloader',
+			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
+			'image' => 'plugins/images/tabs/jdownloader.png',
+			'category' => 'Downloader',
+			'settings' => array(
+				'custom' => '
 				<div class="row">
                     <div class="col-lg-12">
                         <div class="panel panel-info">
@@ -1073,62 +1073,62 @@ function getHomepageList()
                     </div>
 				</div>
 				',
-                'Enable' => array(
-                    array(
-                        'type' => 'switch',
-                        'name' => 'homepageJdownloaderEnabled',
-                        'label' => 'Enable',
-                        'value' => $GLOBALS['homepageJdownloaderEnabled']
-                    ),
-                    array(
-                        'type' => 'select',
-                        'name' => 'homepageJdownloaderAuth',
-                        'label' => 'Minimum Authentication',
-                        'value' => $GLOBALS['homepageJdownloaderAuth'],
-                        'options' => $groups
-                    )
-                ),
-                'Connection' => array(
-                    array(
-                        'type' => 'input',
-                        'name' => 'jdownloaderURL',
-                        'label' => 'URL',
-                        'value' => $GLOBALS['jdownloaderURL'],
-                        'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-                        'placeholder' => 'http(s)://hostname:port'
-                    )
-                ),
-                'Misc Options' => array(
-                    array(
-                        'type' => 'select',
-                        'name' => 'homepageDownloadRefresh',
-                        'label' => 'Refresh Seconds',
-                        'value' => $GLOBALS['homepageDownloadRefresh'],
-                        'options' => optionTime()
-                    ),
-                    array(
-                        'type' => 'switch',
-                        'name' => 'jdownloaderCombine',
-                        'label' => 'Add to Combined Downloader',
-                        'value' => $GLOBALS['jdownloaderCombine']
-                    ),
-                ),
-                'Test Connection' => array(
-                    array(
-                        'type' => 'blank',
-                        'label' => 'Please Save before Testing'
-                    ),
-                    array(
-                        'type' => 'button',
-                        'label' => '',
-                        'icon' => 'fa fa-flask',
-                        'class' => 'pull-right',
-                        'text' => 'Test Connection',
-                        'attr' => 'onclick="testAPIConnection(\'jdownloader\')"'
-                    ),
-                )
-            )
-        ),
+				'Enable' => array(
+					array(
+						'type' => 'switch',
+						'name' => 'homepageJdownloaderEnabled',
+						'label' => 'Enable',
+						'value' => $GLOBALS['homepageJdownloaderEnabled']
+					),
+					array(
+						'type' => 'select',
+						'name' => 'homepageJdownloaderAuth',
+						'label' => 'Minimum Authentication',
+						'value' => $GLOBALS['homepageJdownloaderAuth'],
+						'options' => $groups
+					)
+				),
+				'Connection' => array(
+					array(
+						'type' => 'input',
+						'name' => 'jdownloaderURL',
+						'label' => 'URL',
+						'value' => $GLOBALS['jdownloaderURL'],
+						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
+						'placeholder' => 'http(s)://hostname:port'
+					)
+				),
+				'Misc Options' => array(
+					array(
+						'type' => 'select',
+						'name' => 'homepageDownloadRefresh',
+						'label' => 'Refresh Seconds',
+						'value' => $GLOBALS['homepageDownloadRefresh'],
+						'options' => optionTime()
+					),
+					array(
+						'type' => 'switch',
+						'name' => 'jdownloaderCombine',
+						'label' => 'Add to Combined Downloader',
+						'value' => $GLOBALS['jdownloaderCombine']
+					),
+				),
+				'Test Connection' => array(
+					array(
+						'type' => 'blank',
+						'label' => 'Please Save before Testing'
+					),
+					array(
+						'type' => 'button',
+						'label' => '',
+						'icon' => 'fa fa-flask',
+						'class' => 'pull-right',
+						'text' => 'Test Connection',
+						'attr' => 'onclick="testAPIConnection(\'jdownloader\')"'
+					),
+				)
+			)
+		),
 		array(
 			'name' => 'SabNZBD',
 			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
@@ -2577,6 +2577,15 @@ function getHomepageList()
 						'options' => $groups
 					)
 				),
+				'Options' => array(
+					array(
+						'type' => 'input',
+						'name' => 'tautulliHeader',
+						'label' => 'Title',
+						'value' => $GLOBALS['tautulliHeader'],
+						'help' => 'Sets the title of this homepage module'
+					)
+				),
 				'Connection' => array(
 					array(
 						'type' => 'input',
@@ -2654,7 +2663,6 @@ function getHomepageList()
 					),
 				),
 				'Misc Stats' => array(
-					
 					array(
 						'type' => 'switch',
 						'name' => 'tautulliTopUsers',
@@ -2717,13 +2725,13 @@ function buildHomepageSettings()
 					$class .= ' faded';
 				}
 				break;
-            case 'homepageOrderjdownloader':
-                $class = 'bg-sab';
-                $image = 'plugins/images/tabs/jdownloader.png';
-                if (!$GLOBALS['homepageJdownloaderEnabled']) {
-                    $class .= ' faded';
-                }
-                break;
+			case 'homepageOrderjdownloader':
+				$class = 'bg-sab';
+				$image = 'plugins/images/tabs/jdownloader.png';
+				if (!$GLOBALS['homepageJdownloaderEnabled']) {
+					$class .= ' faded';
+				}
+				break;
 			case 'homepageOrdersabnzbd':
 				$class = 'bg-sab';
 				$image = 'plugins/images/tabs/sabnzbd.png';
@@ -2805,12 +2813,12 @@ function buildHomepageSettings()
 				}
 				break;
 			case 'homepageOrdertautulli':
-					$class = 'bg-info';
-					$image = 'plugins/images/tabs/tautulli.png';
-					if (!$GLOBALS['homepageTautulliEnabled']) {
-						$class .= ' faded';
-					}
-					break;
+				$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';

+ 5 - 5
api/functions/normal-functions.php

@@ -313,14 +313,14 @@ function getCert()
 	$file = __DIR__ . DIRECTORY_SEPARATOR . 'cert' . DIRECTORY_SEPARATOR . 'cacert.pem';
 	$file2 = __DIR__ . DIRECTORY_SEPARATOR . 'cert' . DIRECTORY_SEPARATOR . 'cacert-initial.pem';
 	$useCert = (file_exists($file)) ? $file : $file2;
-	if($GLOBALS['selfSignedCert'] !== ''){
-		if(file_exists($GLOBALS['selfSignedCert'])){
+	if ($GLOBALS['selfSignedCert'] !== '') {
+		if (file_exists($GLOBALS['selfSignedCert'])) {
 			return $GLOBALS['selfSignedCert'];
 		}
 	}
 	$context = stream_context_create(
 		array(
-			'ssl'=> array(
+			'ssl' => array(
 				'verify_peer' => true,
 				'cafile' => $useCert
 			)
@@ -420,7 +420,7 @@ function download($url, $path)
 
 function localURL($url, $force = false)
 {
-	if($force){
+	if ($force) {
 		return true;
 	}
 	if (strpos($url, 'https') !== false) {
@@ -764,4 +764,4 @@ function formatSeconds($seconds)
 	}
 	//return $timeExtra[0] . 's ' . (number_format(('0.' . substr($timeExtra[1], 0, 4)), 4, '.', '') * 1000) . 'ms';
 	//return (number_format(('0.' . substr($timeExtra[1], 0, 4)), 4, '.', '') * 1000) . 'ms';
-}
+}

+ 101 - 95
api/functions/organizr-functions.php

@@ -44,6 +44,9 @@ function organizrSpecialSettings()
 			'options' => array(
 				'alternateHomepageHeaders' => $GLOBALS['alternateHomepageHeaders'],
 				'healthChecksTags' => $GLOBALS['healthChecksTags'],
+				'titles' => array(
+					'tautulli' => $GLOBALS['tautulliHeader']
+				)
 			),
 			'media' => array(
 				'jellyfin' => (strpos($GLOBALS['embyURL'], 'jellyfin') !== false) ? true : false
@@ -1668,7 +1671,7 @@ function auth()
 	$group = 0;
 	$groupParam = $_GET['group'];
 	$redirect = false;
-	if(isset($groupParam)) {
+	if (isset($groupParam)) {
 		if (is_numeric($groupParam)) {
 			$group = (int)$groupParam;
 		} else {
@@ -1713,18 +1716,18 @@ function auth()
 	}
 }
 
-function getTabGroup ($tab)
+function getTabGroup($tab)
 {
-    try {
-        $connect = new Dibi\Connection([
-            'driver' => 'sqlite3',
-            'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],]);
-        $row = $connect->fetch('SELECT group_id FROM tabs WHERE name LIKE %~like~', $tab);
-        return $row ? $row['group_id'] : 0;
-    } catch (\Dibi\Exception $e) {
-        writeLog('error', 'Tab Group Function - Error Fetching Tab Group', $tab);
-        return 0;
-    }
+	try {
+		$connect = new Dibi\Connection([
+			'driver' => 'sqlite3',
+			'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],]);
+		$row = $connect->fetch('SELECT group_id FROM tabs WHERE name LIKE %~like~', $tab);
+		return $row ? $row['group_id'] : 0;
+	} catch (\Dibi\Exception $e) {
+		writeLog('error', 'Tab Group Function - Error Fetching Tab Group', $tab);
+		return 0;
+	}
 }
 
 function logoOrText()
@@ -2062,12 +2065,15 @@ function getImage()
 
 function cacheImage($url, $name, $extension = 'jpg')
 {
-    $cacheDirectory = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
-    if (!file_exists($cacheDirectory)) {
-        mkdir($cacheDirectory, 0777, true);
-    }
-    $cachefile = $cacheDirectory . $name . '.' . $extension;
-    @copy($url, $cachefile);
+	$cacheDirectory = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
+	if (!file_exists($cacheDirectory)) {
+		mkdir($cacheDirectory, 0777, true);
+	}
+	$cachefile = $cacheDirectory . $name . '.' . $extension;
+	$cachetime = 604800;
+	if ((file_exists($cachefile) && time() - $cachetime < filemtime($cachefile)) || !file_exists($cachefile)) {
+		@copy($url, $cachefile);
+	}
 }
 
 function downloader($array)
@@ -2084,34 +2090,34 @@ function downloader($array)
 					break;
 			}
 			break;
-        case 'jdownloader':
-            switch ($array['data']['action']) {
-                case 'start':
-                    jdownloaderAction($array['data']['action'], $array['data']['target']);
-                    break;
-                case 'stop':
-                    jdownloaderAction($array['data']['action'], $array['data']['target']);
-                    break;
-                case 'resume':
-                    jdownloaderAction($array['data']['action'], $array['data']['target']);
-                    break;
-                case 'pause':
-                    jdownloaderAction($array['data']['action'], $array['data']['target']);
-                    break;
-                case 'update':
-                    jdownloaderAction($array['data']['action'], $array['data']['target']);
-                    break;
-                case 'retry':
-                    jdownloaderAction($array['data']['action'], $array['data']['target']);
-                    break;
-                case 'remove':
-                    jdownloaderAction($array['data']['action'], $array['data']['target']);
-                    break;
-                default:
-                    # code...
-                    break;
-            }
-            break;
+		case 'jdownloader':
+			switch ($array['data']['action']) {
+				case 'start':
+					jdownloaderAction($array['data']['action'], $array['data']['target']);
+					break;
+				case 'stop':
+					jdownloaderAction($array['data']['action'], $array['data']['target']);
+					break;
+				case 'resume':
+					jdownloaderAction($array['data']['action'], $array['data']['target']);
+					break;
+				case 'pause':
+					jdownloaderAction($array['data']['action'], $array['data']['target']);
+					break;
+				case 'update':
+					jdownloaderAction($array['data']['action'], $array['data']['target']);
+					break;
+				case 'retry':
+					jdownloaderAction($array['data']['action'], $array['data']['target']);
+					break;
+				case 'remove':
+					jdownloaderAction($array['data']['action'], $array['data']['target']);
+					break;
+				default:
+					# code...
+					break;
+			}
+			break;
 		case 'nzbget':
 			break;
 		default:
@@ -2122,53 +2128,51 @@ function downloader($array)
 
 function jdownloaderAction($action = null, $target = null)
 {
-    if ($GLOBALS['homepageJdownloaderEnabled'] && !empty($GLOBALS['jdownloaderURL']) && qualifyRequest($GLOBALS['homepageJdownloaderAuth'])) {
-        $url = qualifyURL($GLOBALS['jdownloaderURL']);
-
-        # This ensures compatibility with RSScrawler
-        $url = str_replace('/myjd', '', $url);
-        if(substr($url , -1)=='/') {
-            $url = substr_replace($url ,"",-1);
-        }
-
-        switch ($action) {
-            case 'start':
-                $url = $url . '/myjd_start/';
-                break;
-            case 'stop':
-                $url = $url . '/myjd_stop/';
-                break;
-            case 'resume':
-                $url = $url . '/myjd_pause/false';
-                break;
-            case 'pause':
-                $url = $url . '/myjd_pause/true';
-                break;
-            case 'update':
-                $url = $url . '/myjd_update';
-                break;
-            case 'retry':
-                # code...
-                break;
-            case 'remove':
-                # code...
-                break;
-            default:
-                # code...
-                break;
-        }
-        try {
-            $options = (localURL($url)) ? array('verify' => false) : array();
-            $response = Requests::post($url, array(), $options);
-            if ($response->success) {
-                $api['content'] = json_decode($response->body, true);
-            }
-        } catch (Requests_Exception $e) {
-            writeLog('error', 'JDownloader Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-        };
-        $api['content'] = isset($api['content']) ? $api['content'] : false;
-        return $api;
-    }
+	if ($GLOBALS['homepageJdownloaderEnabled'] && !empty($GLOBALS['jdownloaderURL']) && qualifyRequest($GLOBALS['homepageJdownloaderAuth'])) {
+		$url = qualifyURL($GLOBALS['jdownloaderURL']);
+		# This ensures compatibility with RSScrawler
+		$url = str_replace('/myjd', '', $url);
+		if (substr($url, -1) == '/') {
+			$url = substr_replace($url, "", -1);
+		}
+		switch ($action) {
+			case 'start':
+				$url = $url . '/myjd_start/';
+				break;
+			case 'stop':
+				$url = $url . '/myjd_stop/';
+				break;
+			case 'resume':
+				$url = $url . '/myjd_pause/false';
+				break;
+			case 'pause':
+				$url = $url . '/myjd_pause/true';
+				break;
+			case 'update':
+				$url = $url . '/myjd_update';
+				break;
+			case 'retry':
+				# code...
+				break;
+			case 'remove':
+				# code...
+				break;
+			default:
+				# code...
+				break;
+		}
+		try {
+			$options = (localURL($url)) ? array('verify' => false) : array();
+			$response = Requests::post($url, array(), $options);
+			if ($response->success) {
+				$api['content'] = json_decode($response->body, true);
+			}
+		} catch (Requests_Exception $e) {
+			writeLog('error', 'JDownloader Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
+		};
+		$api['content'] = isset($api['content']) ? $api['content'] : false;
+		return $api;
+	}
 }
 
 function sabnzbdAction($action = null, $target = null)
@@ -2657,21 +2661,23 @@ function checkHostPrefix($s)
 	}
 	return (substr($s, -1, 1) == '\\') ? $s : $s . '\\';
 }
+
 function analyzeIP($ip)
 {
-	if(strpos($ip,'/') !== false){
+	if (strpos($ip, '/') !== false) {
 		$explodeIP = explode('/', $ip);
 		$prefix = $explodeIP[1];
 		$start_ip = $explodeIP[0];
 		$ip_count = 1 << (32 - $prefix);
 		$start_ip_long = ip2long($start_ip);
 		$last_ip_long = ip2long($start_ip) + $ip_count - 1;
-	}elseif(substr_count($ip, '.') == 3){
+	} elseif (substr_count($ip, '.') == 3) {
 		$start_ip_long = ip2long($ip);
 		$last_ip_long = ip2long($ip);
 	}
 	return (isset($start_ip_long) && isset($last_ip_long)) ? array('from' => $start_ip_long, 'to' => $last_ip_long) : false;
 }
+
 function authProxyRangeCheck($from, $to)
 {
 	$approved = false;

+ 2 - 2
api/functions/sso-functions.php

@@ -66,8 +66,8 @@ function getTautulliToken($username, $password, $plexToken = null)
 					"User-Agent" => isset($_SERVER ['HTTP_USER_AGENT']) ? $_SERVER ['HTTP_USER_AGENT'] : null
 				);
 				$data = array(
-	                "username" => ($plexToken ? "" : $username),
-	                "password" => ($plexToken ? "" : $password),
+					"username" => ($plexToken ? "" : $username),
+					"password" => ($plexToken ? "" : $password),
 					"token" => $plexToken,
 					"remember_me" => 1,
 				);

+ 5 - 0
css/organizr.css

@@ -1278,6 +1278,11 @@ span.fc-title {
     position: absolute;
     top: 15px;
 }
+@media(max-width:767px) {
+    .show-sidebar .sidebar {
+        overflow-y: auto !important;
+    }
+}
 .nav-second-level .sidebar-tabName {
     position: absolute;
     top: 20px;

File diff suppressed because it is too large
+ 0 - 0
css/organizr.min.css


+ 16 - 16
js/functions.js

@@ -4391,7 +4391,7 @@ function buildStream(array, type){
 	<div id="`+type+`Streams">
 		<div class="el-element-overlay row">
 		    <div class="col-md-12">
-		        <h4 class="pull-left homepage-element-title"><span lang="en">Active</span> `+toUpper(type)+` <span lang="en">Streams</span>: </h4><h4 class="pull-left">&nbsp;<span class="label label-info m-l-20 checkbox-circle mouse" onclick="homepageStream('`+type+`')">`+streams+`</span></h4>
+		        <h4 class="pull-left homepage-element-title"><span lang="en">Active</span> `+toUpper(type)+` <span lang="en">Streams</span> : </h4><h4 class="pull-left">&nbsp;<span class="label label-info m-l-20 checkbox-circle mouse" onclick="homepageStream('`+type+`')">`+streams+`</span></h4>
 		        <hr class="hidden-xs">
 		    </div>
 			<div class="clearfix"></div>
@@ -4434,7 +4434,7 @@ function buildRecent(array, type){
 	if(activeInfo.settings.homepage.options.alternateHomepageHeaders){
 		var headerAlt = `
 		<div class="col-md-12">
-			<h4 class="pull-left homepage-element-title"><span class="mouse" onclick="homepageRecent('`+type+`')" lang="en">Recently Added</span></h4>
+			<h4 class="pull-left homepage-element-title"><span class="mouse" onclick="homepageRecent('`+type+`')" lang="en">Recently Added</span> : </h4><h4 class="pull-left">&nbsp;</h4>
 			`+dropdownMenu+`
 			<hr class="hidden-xs"><div class="clearfix"></div>
 		</div>
@@ -4529,7 +4529,7 @@ function buildPlaylist(array, type){
 	if(activeInfo.settings.homepage.options.alternateHomepageHeaders){
 		var headerAlt = `
 		<div class="col-md-12">
-			<h4 class="pull-left homepage-element-title"><span onclick="homepagePlaylist('`+type+`')" class="`+type+`-playlistTitle mouse">`+first+`</span></h4>
+			<h4 class="pull-left homepage-element-title"><span onclick="homepagePlaylist('`+type+`')" class="`+type+`-playlistTitle mouse">`+first+`</span> : </h4><h4 class="pull-left">&nbsp;</h4>
 			<div class="btn-group pull-right">
 				`+builtDropdown+`
 			</div>
@@ -4612,7 +4612,7 @@ function buildRequest(array){
 	if(activeInfo.settings.homepage.options.alternateHomepageHeaders){
 		var headerAlt = `
 		<div class="col-md-12">
-			<h4 class="pull-left homepage-element-title"><span class="mouse" onclick="homepageRequests()" lang="en">Requests</span></h4>
+			<h4 class="pull-left homepage-element-title"><span class="mouse" onclick="homepageRequests()" lang="en">Requests</span> : </h4><h4 class="pull-left">&nbsp;</h4>
 			<div class="btn-group pull-right">
 				`+builtDropdown+`
 			</div>
@@ -5684,7 +5684,7 @@ function buildPihole(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">Pi-hole</span></h4>
+		        <h4 class="pull-left homepage-element-title"><span lang="en">Pi-hole</span> : </h4><h4 class="pull-left">&nbsp;</h4>
 		        <hr class="hidden-xs ml-2">
 		    </div>
 			<div class="clearfix"></div>
@@ -6382,7 +6382,6 @@ function homepageCalendar(timeout){
 	timeouts['calendar-Homepage'] = setTimeout(function(){ homepageCalendar(timeout); }, timeout);
 }
 function buildTautulliItem(array){
-    console.log(array);
     var cards = `
     <style>
     .homepage-tautulli-card {
@@ -6465,6 +6464,7 @@ function buildTautulliItem(array){
         top: 0;
         left: 0;
         filter: blur(7px) brightness(30%);
+        transform: scale(1.1);
     }
 
     .lib-stats-row::before {
@@ -6540,15 +6540,15 @@ function buildTautulliItem(array){
                 <div class="card text-white mb-3 homepage-tautulli-card library-card card-bg-colour">
                     <div class="card-body h-100">
                         <div class="row h-100" style="display: flex;">
-                            <div class="col-lg-4 col-md-4 col-sm-4 hidden-xs align-self-center">`;
+                            <div class="col-lg-4 col-md-4 col-sm-4 col-xs-4 align-self-center" style="overflow: hidden;">`;
                             if(type == 'artist') {
-                                card += `<img src="/plugins/images/cache/tautulli-`+type+`.jpg" class="lib-icon" alt="library icon">`;
+                                card += `<img src="plugins/images/cache/tautulli-`+type+`.png" class="lib-icon" alt="library icon">`;
                             } else {
-                                card += `<img src="/plugins/images/cache/tautulli-`+type+`.svg" class="lib-icon" alt="library icon">`;
+                                card += `<img src="plugins/images/cache/tautulli-`+type+`.svg" class="lib-icon" alt="library icon">`;
                             }
             card += `
                             </div>
-                            <div class="col-lg-8 col-md-8 col-sm-8 col-xs-12">
+                            <div class="col-lg-8 col-md-8 col-sm-8 col-xs-12" style="overflow: hidden;">
                                 <ol class="h-100">`;
                                 data.forEach(e => {
                                     card += `<li class="w-100">
@@ -6588,7 +6588,7 @@ function buildTautulliItem(array){
                     classes = '';
                 }
                 card += `
-                <div class=col-lg-4 col-md-6 col-sm-12 col-xs-12">
+                <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 += `
@@ -6600,21 +6600,21 @@ function buildTautulliItem(array){
                 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">`;
+                                <div class="col-lg-4 col-md-4 col-sm-4 col-xs-4 align-self-center" style="overflow: hidden;">`;
                                 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="/plugins/images/cache/tautulli-`+e['rows'][0]['platform_name']+`.svg" class="poster" alt="platform icon">`;
+                                    card += `<img src="plugins/images/cache/tautulli-`+e['rows'][0]['platform_name']+`.svg" class="poster" alt="platform icon">`;
                                 } else {
                                     card += `<img src="`+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">
+                                <div class="col-lg-8 col-md-8 col-sm-8 col-xs-12" style="overflow: hidden;">
                                     <h4>`+e['stat_title']+`</h4>
                                     <hr class="my-2">
                                     <ol class="pl-2">`;
-                                    for(var i = 0; i < 5; i++) {
+                                    for(var i = 0; i < Math.min(5, e['rows'].length); i++) {
                                         var item = e['rows'][i];
                                         if(stat == 'top_users') {
                                             card += `<li><p class="one-line">`+item['user']+`</p></li>`;
@@ -6657,7 +6657,7 @@ function buildTautulli(array){
     <div id="allTautulli">
 		<div class="el-element-overlay row">
 		    <div class="col-md-12">
-		        <h4 class="pull-left homepage-element-title"><span lang="en">Tautulli</span></h4>
+		        <h4 class="pull-left homepage-element-title"><span>`+activeInfo.settings.homepage.options.titles.tautulli+`</span> : </h4><h4 class="pull-left">&nbsp;</h4>
 		        <hr class="hidden-xs ml-2">
 		    </div>
 			<div class="clearfix"></div>

Some files were not shown because too many files changed in this diff