Browse Source

Merge pull request #1304 from causefx/v2-develop

V2 develop
causefx 6 năm trước cách đây
mục cha
commit
e126646872

+ 2 - 0
api/config/default.php

@@ -86,6 +86,7 @@ return array(
 	'qBittorrentSortOrder' => 'eta',
 	'qBittorrentReverseSorting' => false,
 	'qBittorrentCombine' => false,
+	'qBittorrentApiVersion' => '1',
 	'rTorrentURL' => '',
 	'rTorrentURLOverride' => '',
 	'rTorrentUsername' => '',
@@ -95,6 +96,7 @@ return array(
 	'rTorrentSortOrder' => 'datea',
 	'rTorrentReverseSorting' => false,
 	'rTorrentCombine' => false,
+	'rTorrentDisableCertCheck' => false,
 	'homepageCalendarEnabled' => 'true',
 	'homepageCalendarAuth' => '4',
 	'calendariCal' => '',

+ 11 - 4
api/functions/api-functions.php

@@ -213,7 +213,7 @@ function login($array)
 				if ($createToken) {
 					writeLoginLog($username, 'success');
 					writeLog('success', 'Login Function - A User has logged in', $username);
-					$ssoUser = (empty($result['email'])) ? $result['username'] : (strpos($result['email'], 'placeholder') !== false) ? $result['username'] : $result['email'];
+					$ssoUser = ((empty($result['email'])) ? $result['username'] : (strpos($result['email'], 'placeholder') !== false)) ? $result['username'] : $result['email'];
 					ssoCheck($ssoUser, $password, $token); //need to work on this
 					return ($output) ? array('name' => $GLOBALS['cookieName'], 'token' => (string)$createToken) : true;
 				} else {
@@ -1196,9 +1196,9 @@ function allGroups()
 	return false;
 }
 
-function loadTabs()
+function loadTabs($type = null)
 {
-	if (file_exists('config' . DIRECTORY_SEPARATOR . 'config.php')) {
+	if (file_exists('config' . DIRECTORY_SEPARATOR . 'config.php') && $type) {
 		try {
 			$connect = new Dibi\Connection([
 				'driver' => 'sqlite3',
@@ -1219,7 +1219,14 @@ function loadTabs()
 				$v['count'] = isset($count[$v['category_id']]) ? $count[$v['category_id']] : 0;
 			}
 			$all['categories'] = $categories;
-			return $all;
+			switch ($type){
+				case 'categories':
+					return $all['categories'];
+				case 'tabs':
+					return $all['tabs'];
+				default:
+					return $all;
+			}
 		} catch (Dibi\Exception $e) {
 			return false;
 		}

+ 41 - 20
api/functions/homepage-connect-functions.php

@@ -128,6 +128,7 @@ function streamType($value)
 
 function resolveEmbyItem($itemDetails)
 {
+	/*
 	// Grab Each item info from Emby (extra call)
 	$id = isset($itemDetails['NowPlayingItem']['Id']) ? $itemDetails['NowPlayingItem']['Id'] : $itemDetails['Id'];
 	$url = qualifyURL($GLOBALS['embyURL']);
@@ -141,6 +142,8 @@ function resolveEmbyItem($itemDetails)
 	} catch (Requests_Exception $e) {
 		return false;
 	};
+	*/
+	$item = isset($itemDetails['NowPlayingItem']['Id']) ? $itemDetails['NowPlayingItem'] : $itemDetails;
 	// Static Height & Width
 	$height = getCacheImageSize('h');
 	$width = getCacheImageSize('w');
@@ -248,7 +251,8 @@ function resolveEmbyItem($itemDetails)
 	$embyItem['user'] = ($GLOBALS['homepageShowStreamNames'] && qualifyRequest($GLOBALS['homepageShowStreamNamesAuth'])) ? @(string)$itemDetails['UserName'] : "";
 	$embyItem['userThumb'] = '';
 	$embyItem['userAddress'] = (isset($itemDetails['RemoteEndPoint']) ? $itemDetails['RemoteEndPoint'] : "x.x.x.x");
-	$embyItem['address'] = $GLOBALS['embyTabURL'] ? rtrim($GLOBALS['embyTabURL'], '/') . "/web/#!/itemdetails.html?id=" . $embyItem['uid'] : "https://app.emby.media/#!/itemdetails.html?id=" . $embyItem['uid'] . "&serverId=" . $embyItem['id'];
+	$embyURL = (strpos($GLOBALS['embyURL'], 'jellyfin') !== false) ? $GLOBALS['embyURL'] . '/web/index.html#!/itemdetails.html?id=' : 'https://app.emby.media/#!/itemdetails.html?id=';
+	$embyItem['address'] = $GLOBALS['embyTabURL'] ? rtrim($GLOBALS['embyTabURL'], '/') . "/web/#!/itemdetails.html?id=" . $embyItem['uid'] : $embyURL . $embyItem['uid'] . "&serverId=" . $embyItem['id'];
 	$embyItem['nowPlayingOriginalImage'] = 'api/?v1/image&source=emby&type=' . $embyItem['nowPlayingImageType'] . '&img=' . $embyItem['nowPlayingThumb'] . '&height=' . $nowPlayingHeight . '&width=' . $nowPlayingWidth . '&key=' . $embyItem['nowPlayingKey'] . '$' . randString();
 	$embyItem['originalImage'] = 'api/?v1/image&source=emby&type=' . $embyItem['imageType'] . '&img=' . $embyItem['thumb'] . '&height=' . $height . '&width=' . $width . '&key=' . $embyItem['key'] . '$' . randString();
 	$embyItem['openTab'] = $GLOBALS['embyTabURL'] && $GLOBALS['embyTabName'] ? true : false;
@@ -369,6 +373,20 @@ function resolvePlexItem($item)
 	$cacheDirectoryWeb = 'plugins/images/cache/';
 	// Types
 	switch ($item['type']) {
+		case 'show':
+			$plexItem['type'] = 'tv';
+			$plexItem['title'] = (string)$item['title'];
+			$plexItem['secondaryTitle'] = (string)$item['year'];
+			$plexItem['summary'] = (string)$item['summary'];
+			$plexItem['ratingKey'] = (string)$item['ratingKey'];
+			$plexItem['thumb'] = (string)$item['thumb'];
+			$plexItem['key'] = (string)$item['ratingKey'] . "-list";
+			$plexItem['nowPlayingThumb'] = (string)$item['art'];
+			$plexItem['nowPlayingKey'] = (string)$item['ratingKey'] . "-np";
+			$plexItem['nowPlayingTitle'] = (string)$item['title'];
+			$plexItem['nowPlayingBottom'] = (string)$item['year'];
+			$plexItem['metadataKey'] = (string)$item['ratingKey'];
+			break;
 		case 'season':
 			$plexItem['type'] = 'tv';
 			$plexItem['title'] = (string)$item['parentTitle'];
@@ -438,6 +456,7 @@ function resolvePlexItem($item)
 			$plexItem['nowPlayingBottom'] = (string)$item['year'];
 			$plexItem['metadataKey'] = (string)$item['ratingKey'];
 	}
+	$plexItem['originalType'] = $item['type'];
 	$plexItem['uid'] = (string)$item['ratingKey'];
 	$plexItem['elapsed'] = isset($item['viewOffset']) && $item['viewOffset'] !== '0' ? (int)$item['viewOffset'] : null;
 	$plexItem['duration'] = isset($item['duration']) ? (int)$item['duration'] : (int)$item->Media['duration'];
@@ -464,7 +483,7 @@ function resolvePlexItem($item)
 		'platform' => (string)$item->Player['platform'],
 		'product' => (string)$item->Player['product'],
 		'device' => (string)$item->Player['device'],
-		'stream' => (string)$item->Media->Part['decision'] . ($item->TranscodeSession['throttled'] == '1' ? ' (Throttled)' : ''),
+		'stream' => isset($item->Media) ? (string)$item->Media->Part['decision'] . ($item->TranscodeSession['throttled'] == '1' ? ' (Throttled)' : '') : '',
 		'videoResolution' => (string)$item->Media['videoResolution'],
 		'throttled' => ($item->TranscodeSession['throttled'] == 1) ? true : false,
 		'sourceVideoCodec' => (string)$item->TranscodeSession['sourceVideoCodec'],
@@ -668,15 +687,16 @@ function getPlexPlaylists()
 	return false;
 }
 
-function embyConnect($action, $key = null, $skip = false)
+function embyConnect($action, $key = 'Latest', $skip = false)
 {
 	if ($GLOBALS['homepageEmbyEnabled'] && !empty($GLOBALS['embyURL']) && !empty($GLOBALS['embyToken']) && qualifyRequest($GLOBALS['homepageEmbyAuth'])) {
 		$url = qualifyURL($GLOBALS['embyURL']);
 		switch ($action) {
 			case 'streams':
-				$url = $url . '/Sessions?api_key=' . $GLOBALS['embyToken'];
+				$url = $url . '/Sessions?api_key=' . $GLOBALS['embyToken'] . '&Fields=Overview,People,Genres,CriticRating,Studios,Taglines';
 				break;
 			case 'recent':
+			case 'metadata':
 				$username = false;
 				if (isset($GLOBALS['organizrUser']['username'])) {
 					$username = strtolower($GLOBALS['organizrUser']['username']);
@@ -699,35 +719,34 @@ function embyConnect($action, $key = null, $skip = false)
 								break;
 							}
 						}
-						$url = $url . '/Users/' . $userId . '/Items/Latest?EnableImages=false&Limit=' . $GLOBALS['homepageRecentLimit'] . '&api_key=' . $GLOBALS['embyToken'] . ($showPlayed ? '' : '&IsPlayed=false');
+						$url = $url . '/Users/' . $userId . '/Items/' . $key . '?EnableImages=true&Limit=' . $GLOBALS['homepageRecentLimit'] . '&api_key=' . $GLOBALS['embyToken'] . ($showPlayed ? '' : '&IsPlayed=false') . '&Fields=Overview,People,Genres,CriticRating,Studios,Taglines';
 					}
 				} catch (Requests_Exception $e) {
 					writeLog('error', 'Emby Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
 				};
 				break;
-			case 'metadata':
-				$skip = true;
-				break;
 			default:
 				# code...
 				break;
 		}
-		if ($skip && $key) {
-			$items[] = resolveEmbyItem(array('Id' => $key));
-			$api['content'] = $items;
-			return $api;
-		}
 		try {
 			$options = (localURL($url)) ? array('verify' => false) : array();
 			$response = Requests::get($url, array(), $options);
 			if ($response->success) {
 				$items = array();
 				$emby = json_decode($response->body, true);
-				foreach ($emby as $child) {
-					if (isset($child['NowPlayingItem']) || isset($child['Name'])) {
-						$items[] = resolveEmbyItem($child);
+				if($key !== 'Latest'){
+					if (isset($emby['NowPlayingItem']) || isset($emby['Name'])) {
+						$items[] = resolveEmbyItem($emby);
+					}
+				}else{
+					foreach ($emby as $child) {
+						if (isset($child['NowPlayingItem']) || isset($child['Name'])) {
+							$items[] = resolveEmbyItem($child);
+						}
 					}
 				}
+				
 				$api['content'] = array_filter($items);
 				return $api;
 			}
@@ -933,7 +952,7 @@ function rTorrentConnect()
 			$extraPath = (strpos($GLOBALS['rTorrentURL'], '.php') !== false) ? '' : '/RPC2';
 			$extraPath = (empty($GLOBALS['rTorrentURLOverride'])) ? $extraPath : '';
 			$url = $digest['scheme'] . '://' . $passwordInclude . $digest['host'] . $digest['port'] . $digest['path'] . $extraPath;
-			$options = (localURL($url)) ? array('verify' => false) : array();
+			$options = (localURL($url, $GLOBALS['rTorrentDisableCertCheck'])) ? array('verify' => false) : array();
 			$data = xmlrpc_encode_request("d.multicall2", array(
 				"",
 				"main",
@@ -1031,7 +1050,9 @@ function qBittorrentConnect()
 	if ($GLOBALS['homepageqBittorrentEnabled'] && !empty($GLOBALS['qBittorrentURL']) && qualifyRequest($GLOBALS['homepageqBittorrentAuth'])) {
 		$digest = qualifyURL($GLOBALS['qBittorrentURL'], true);
 		$data = array('username' => $GLOBALS['qBittorrentUsername'], 'password' => decrypt($GLOBALS['qBittorrentPassword']));
-		$url = $digest['scheme'] . '://' . $digest['host'] . $digest['port'] . $digest['path'] . '/login';
+		$apiVersionLogin = ($GLOBALS['qBittorrentApiVersion'] == '1') ? '/login' : '/api/v2/auth/login';
+		$apiVersionQuery = ($GLOBALS['qBittorrentApiVersion'] == '1') ? '/query/torrents?sort=' : '/api/v2/torrents/info?sort=';
+		$url = $digest['scheme'] . '://' . $digest['host'] . $digest['port'] . $digest['path'] . $apiVersionLogin;
 		try {
 			$options = (localURL($GLOBALS['qBittorrentURL'])) ? array('verify' => false) : array();
 			$response = Requests::post($url, array(), $data, $options);
@@ -1044,7 +1065,7 @@ function qBittorrentConnect()
 					'Cookie' => 'SID=' . $cookie['SID']->value
 				);
 				$reverse = $GLOBALS['qBittorrentReverseSorting'] ? 'true' : 'false';
-				$url = $digest['scheme'] . '://' . $digest['host'] . $digest['port'] . $digest['path'] . '/query/torrents?sort=' . $GLOBALS['qBittorrentSortOrder'] . '&reverse=' . $reverse;
+				$url = $digest['scheme'] . '://' . $digest['host'] . $digest['port'] . $digest['path'] . $apiVersionQuery . $GLOBALS['qBittorrentSortOrder'] . '&reverse=' . $reverse;
 				$response = Requests::get($url, $headers, $options);
 				if ($response) {
 					$torrentList = json_decode($response->body, true);
@@ -2632,7 +2653,7 @@ function testAPIConnection($array)
 					$extraPath = (strpos($GLOBALS['rTorrentURL'], '.php') !== false) ? '' : '/RPC2';
 					$extraPath = (empty($GLOBALS['rTorrentURLOverride'])) ? $extraPath : '';
 					$url = $digest['scheme'] . '://' . $passwordInclude . $digest['host'] . $digest['port'] . $digest['path'] . $extraPath;
-					$options = (localURL($url)) ? array('verify' => false) : array();
+					$options = (localURL($url, $GLOBALS['rTorrentDisableCertCheck'])) ? array('verify' => false) : array();
 					$data = xmlrpc_encode_request("system.listMethods", null);
 					$response = Requests::post($url, array(), $data, $options);
 					if ($response->success) {

+ 29 - 5
api/functions/homepage-functions.php

@@ -527,6 +527,16 @@ function getHomepageList()
 			'value' => 'statusa'
 		),
 	);
+	$qBittorrentApiOptions = array(
+		array(
+			'name' => 'V1',
+			'value' => '1'
+		),
+		array(
+			'name' => 'V2',
+			'value' => '2'
+		),
+	);
 	$qBittorrentSortOptions = array(
 		array(
 			'name' => 'Hash',
@@ -872,7 +882,7 @@ function getHomepageList()
 			)
 		),
 		array(
-			'name' => 'Emby',
+			'name' => 'Emby-Jellyfin',
 			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
 			'image' => 'plugins/images/tabs/emby.png',
 			'category' => 'Media Server',
@@ -898,8 +908,8 @@ function getHomepageList()
 						'name' => 'embyURL',
 						'label' => 'URL',
 						'value' => $GLOBALS['embyURL'],
-						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
+						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too. Make sure if Jelly fin to end url with /jellyfin',
+						'placeholder' => 'http(s)://hostname:port - make sure if Jelly fin to end url with /jellyfin'
 					),
 					array(
 						'type' => 'password-alt',
@@ -1337,6 +1347,13 @@ function getHomepageList()
 						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
 						'placeholder' => 'http(s)://hostname:port'
 					),
+					array(
+						'type' => 'select',
+						'name' => 'qBittorrentApiVersion',
+						'label' => 'API Version',
+						'value' => $GLOBALS['qBittorrentApiVersion'],
+						'options' => $qBittorrentApiOptions
+					),
 					array(
 						'type' => 'input',
 						'name' => 'qBittorrentUsername',
@@ -1356,7 +1373,8 @@ function getHomepageList()
 						'name' => 'qBittorrentHideSeeding',
 						'label' => 'Hide Seeding',
 						'value' => $GLOBALS['qBittorrentHideSeeding']
-					), array(
+					),
+					array(
 						'type' => 'switch',
 						'name' => 'qBittorrentHideCompleted',
 						'label' => 'Hide Completed',
@@ -1462,7 +1480,13 @@ function getHomepageList()
 						'name' => 'rTorrentPassword',
 						'label' => 'Password',
 						'value' => $GLOBALS['rTorrentPassword']
-					)
+					),
+					array(
+						'type' => 'switch',
+						'name' => 'rTorrentDisableCertCheck',
+						'label' => 'Disable Certificate Check',
+						'value' => $GLOBALS['rTorrentDisableCertCheck']
+					),
 				),
 				'Misc Options' => array(
 					array(

+ 4 - 1
api/functions/normal-functions.php

@@ -408,8 +408,11 @@ function download($url, $path)
 	return (filesize($path) > 0) ? true : false;
 }
 
-function localURL($url)
+function localURL($url, $force = false)
 {
+	if($force){
+		return true;
+	}
 	if (strpos($url, 'https') !== false) {
 		preg_match("/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/", $url, $result);
 		$result = (!empty($result) ? true : false);

+ 31 - 3
api/functions/organizr-functions.php

@@ -44,6 +44,9 @@ function organizrSpecialSettings()
 			'options' => array(
 				'alternateHomepageHeaders' => $GLOBALS['alternateHomepageHeaders'],
 				'healthChecksTags' => $GLOBALS['healthChecksTags'],
+			),
+			'media' => array(
+				'jellyfin' => (strpos($GLOBALS['embyURL'], 'jellyfin') !== false) ? true : false
 			)
 		),
 		'sso' => array(
@@ -872,7 +875,7 @@ function getSettingsMain()
 				'type' => 'switch',
 				'name' => 'authProxyEnabled',
 				'label' => 'Auth Proxy',
-				'help' => 'Enable option to set Auth Poxy Header Login',
+				'help' => 'Enable option to set Auth Proxy Header Login',
 				'value' => $GLOBALS['authProxyEnabled'],
 			),
 			array(
@@ -1605,15 +1608,25 @@ function auth()
 	$ban = isset($_GET['ban']) ? strtoupper($_GET['ban']) : "";
 	$whitelist = isset($_GET['whitelist']) ? $_GET['whitelist'] : false;
 	$blacklist = isset($_GET['blacklist']) ? $_GET['blacklist'] : false;
-	$group = isset($_GET['group']) ? (int)$_GET['group'] : (int)0;
+    $group = 0;
+    $groupParam = $_GET['group'];
+    if(isset($groupParam)) {
+        if (is_numeric($groupParam)) {
+            $group = (int)$groupParam;
+        } else {
+            $group = getTabGroup($groupParam);
+        }
+    }
 	$currentIP = userIP();
 	$unlocked = ($GLOBALS['organizrUser']['locked'] == '1') ? false : true;
 	if (isset($GLOBALS['organizrUser'])) {
 		$currentUser = $GLOBALS['organizrUser']['username'];
 		$currentGroup = $GLOBALS['organizrUser']['groupID'];
+		$currentEmail = $GLOBALS['organizrUser']['email'];
 	} else {
 		$currentUser = 'Guest';
 		$currentGroup = getUserLevel();
+		$currentEmail = 'guest@guest.com';
 	}
 	$userInfo = "User: $currentUser | Group: $currentGroup | IP: $currentIP | Requesting Access to Group $group | Result: ";
 	if ($whitelist) {
@@ -1629,6 +1642,7 @@ function auth()
 	if ($group !== null) {
 		if (qualifyRequest($group) && $unlocked) {
 			header("X-Organizr-User: $currentUser");
+			header("X-Organizr-Email: $currentEmail");
 			!$debug ? exit(http_response_code(200)) : die("$userInfo Authorized");
 		} else {
 			!$debug ? exit(http_response_code(401)) : die("$userInfo Not Authorized");
@@ -1638,6 +1652,20 @@ function auth()
 	}
 }
 
+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;
+    }
+}
+
 function logoOrText()
 {
 	if ($GLOBALS['useLogo'] == false) {
@@ -2583,4 +2611,4 @@ function authProxyRangeCheck($from, $to)
 		$approved = true;
 	}
 	return $approved;
-}
+}

+ 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,
 				);

+ 1 - 1
api/functions/static-globals.php

@@ -1,7 +1,7 @@
 <?php
 // ===================================
 // Organizr Version
-$GLOBALS['installedVersion'] = '2.0.325';
+$GLOBALS['installedVersion'] = '2.0.362';
 // ===================================
 // Quick php Version check
 $GLOBALS['minimumPHP'] = '7.1.3';

+ 2 - 2
api/index.php

@@ -1133,8 +1133,8 @@ switch ($function) {
 				$status['status'] = organizrStatus();
 				$result['appearance'] = loadAppearance();
 				$status['user'] = $GLOBALS['organizrUser'];
-				$status['categories'] = loadTabs()['categories'];
-				$status['tabs'] = loadTabs()['tabs'];
+				$status['categories'] = loadTabs('categories');
+				$status['tabs'] = loadTabs('tabs');
 				$status['plugins'] = array_filter($GLOBALS, function ($k) use ($pluginSearch) {
 					return stripos($k, $pluginSearch) !== false;
 				}, ARRAY_FILTER_USE_KEY);

+ 3 - 0
css/organizr.css

@@ -501,6 +501,9 @@ input#inviteCodeInput {
 .bg-emby {
     background: #4CAF50;
 }
+.bg-jellyfin {
+    background: #a15dc3;
+}
 .bg-healthchecks {
     background: #56b059;
 }

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
css/organizr.min.css


+ 1 - 1
index.php

@@ -1,4 +1,4 @@
-<?php include 'api/functions/static-globals.php'; ?>
+<?php include 'api/functions/static-globals.php';?>
 <!DOCTYPE html>
 <html lang="en">
 

+ 1 - 0
js/custom.js

@@ -1572,6 +1572,7 @@ $(document).on("click", ".metadata-get", function(e) {
             var action = 'getPlexMetadata';
             break;
         case 'emby':
+        case 'jellyfin':
             var action = 'getEmbyMetadata';
             break;
         default:

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
js/custom.min.js


+ 54 - 47
js/functions.js

@@ -751,7 +751,7 @@ function loadNextTab(){
 	if (typeof next !== 'undefined') {
 		var type = $('#page-wrapper').find('.loaded').attr('data-type');
         var parent = $('#menu-'+next).parent();
-        if(parent.hasClass('in') === false){
+        if(parent.hasClass('in') === false && parent.hasClass('nav-second-level')){
             parent.parent().find('a').first().trigger('click')
         }
 		switchTab(next,type);
@@ -996,7 +996,7 @@ function buildPluginsItem(array){
 	$.each(array, function(i,v) {
 		var settingsPage = (v.settings == true) ? `
 		<!-- Plugin Settings Page -->
-		<form id="`+v.idPrefix+`-settings-page" class="mfp-hide white-popup mfp-with-anim addFormTick col-md-10 col-md-offset-1" autocomplete="off">                			
+		<form id="`+v.idPrefix+`-settings-page" class="mfp-hide white-popup mfp-with-anim addFormTick col-md-10 col-md-offset-1" autocomplete="off">
             <div class="panel bg-org panel-info">
                 <div class="panel-heading">
                     <span lang="en">`+v.name+` Settings</span>
@@ -3277,8 +3277,8 @@ function sponsorAbout(id,array){
                                 <div class="comment-center p-t-10">
                                     <div class="comment-body b-none">
                                         <div class="user-img"> <img src="`+array.logo+`" alt="user" class="img-circle"> </div>
-                                        <div class="mail-contnet">
-                                            <h5><a href="`+array.website+`" target="_blank">`+array.company_name+`</a></h5> 
+                                        <div class="mail-content">
+                                            <h5><a href="`+array.website+`" target="_blank">`+array.company_name+`</a></h5>
                                             `+array.about+extraInfo+`
                                          </div>
                                     </div>
@@ -4018,8 +4018,9 @@ function errorPage(error=null,uri=null){
         local('set','uri',uri);
     }
 	//var urlParams = new URLSearchParams(window.location.search);
-	if($.urlParam('error') !== null){
-		local('set','error',$.urlParam('error'));
+
+	if($.urlParam('error') !== null && !isNaN(Number($.urlParam('error')))){
+        local('set','error',$.urlParam('error'));
 	}
     if($.urlParam('return') !== null && activeInfo.user.loggedin !== true){
         local('set','uri',$.urlParam('return'));
@@ -4090,6 +4091,7 @@ function buildStreamItem(array,source){
 	var cards = '';
 	var count = 0;
 	var total = array.length;
+    var sourceIcon = (source === 'jellyfin' && activeInfo.settings.homepage.media.jellyfin) ? 'play' : source;
 	cards += '<div class="flexbox">';
 	$.each(array, function(i,v) {
 		var icon = '';
@@ -4143,7 +4145,7 @@ function buildStreamItem(array,source){
 							<ul class="el-info p-t-20 m-t-20">
 								<li><a class="btn b-none inline-popups" href="#`+v.session+`" data-effect="mfp-zoom-out"><i class="mdi mdi-server-network mdi-24px"></i></a></li>
 								<li><a class="btn b-none metadata-get" data-source="`+source+`" data-key="`+v.metadataKey+`" data-uid="`+v.uid+`"><i class="mdi mdi-information mdi-24px"></i></a></li>
-								<li><a class="btn b-none openTab" data-tab-name="`+v.tabName+`" data-type="`+v.type+`" data-open-tab="`+v.openTab+`" data-url="`+v.address+`" href="javascript:void(0);"><i class=" mdi mdi-`+source+` mdi-24px"></i></a></li>
+								<li><a class="btn b-none openTab" data-tab-name="`+v.tabName+`" data-type="`+v.type+`" data-open-tab="`+v.openTab+`" data-url="`+v.address+`" href="javascript:void(0);"><i class=" mdi mdi-`+sourceIcon+` mdi-24px"></i></a></li>
 								<li><a class="btn b-none refreshImage" data-type="nowPlaying" data-image="`+v.nowPlayingOriginalImage+`" href="javascript:void(0);"><i class="mdi mdi-refresh mdi-24px"></i></a></li>
 								<a class="inline-popups `+v.uid+` hidden" href="#`+v.uid+`-metadata-div" data-effect="mfp-zoom-out"></a>
 							</ul>
@@ -4381,11 +4383,12 @@ function buildRequestItem(array, extra=null){
 }
 function buildStream(array, type){
 	var streams = (typeof array.content !== 'undefined') ? array.content.length : false;
+    type = (type === 'emby' && activeInfo.settings.homepage.media.jellyfin) ? 'jellyfin' : type;
 	return (streams) ? `
 	<div id="`+type+`Streams">
 		<div class="el-element-overlay row">
 		    <div class="col-md-12">
-		        <h4 class="pull-left"><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>
@@ -4407,6 +4410,7 @@ function buildRecent(array, type){
 	var dropdown = '';
 	var header = '';
 	var headerAlt = '';
+	type = (type === 'emby' && activeInfo.settings.homepage.media.jellyfin) ? 'jellyfin' : type;
 	dropdown += (recent && movie) ? `<li><a data-filter="recent-movie" server-filter="`+type+`" href="javascript:void(0);">Movies</a></li>` : '';
 	dropdown += (recent && tv) ? `<li><a data-filter="recent-tv" server-filter="`+type+`" href="javascript:void(0);">Shows</a></li>` : '';
 	dropdown += (recent && video) ? `<li><a data-filter="recent-video" server-filter="`+type+`" href="javascript:void(0);">Videos</a></li>` : '';
@@ -4427,7 +4431,7 @@ function buildRecent(array, type){
 	if(activeInfo.settings.homepage.options.alternateHomepageHeaders){
 		var headerAlt = `
 		<div class="col-md-12">
-			<h4 class="pull-left"><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>
 			`+dropdownMenu+`
 			<hr class="hidden-xs"><div class="clearfix"></div>
 		</div>
@@ -4522,7 +4526,7 @@ function buildPlaylist(array, type){
 	if(activeInfo.settings.homepage.options.alternateHomepageHeaders){
 		var headerAlt = `
 		<div class="col-md-12">
-			<h4 class="pull-left"><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>
 			<div class="btn-group pull-right">
 				`+builtDropdown+`
 			</div>
@@ -4533,7 +4537,7 @@ function buildPlaylist(array, type){
 	}else{
 		var header = `
 		<div class="panel-heading bg-info p-t-10 p-b-10">
-			<span class="pull-left m-t-5 mouse" onclick="homepagePlaylist('`+type+`')"><img class="lazyload homepageImageTitle" data-src="plugins/images/tabs/`+type+`.png"> &nbsp; <span class="`+type+`-playlistTitle">`+first+`</span></span>
+			<span class="pull-left m-t-5 mouse homepage-element-title" onclick="homepagePlaylist('`+type+`')"><img class="lazyload homepageImageTitle" data-src="plugins/images/tabs/`+type+`.png"> &nbsp; <span class="`+type+`-playlistTitle">`+first+`</span></span>
 			<div class="btn-group pull-right">
 					`+builtDropdown+`
 			</div>
@@ -4605,7 +4609,7 @@ function buildRequest(array){
 	if(activeInfo.settings.homepage.options.alternateHomepageHeaders){
 		var headerAlt = `
 		<div class="col-md-12">
-			<h4 class="pull-left"><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>
 			<div class="btn-group pull-right">
 				`+builtDropdown+`
 			</div>
@@ -4616,7 +4620,7 @@ function buildRequest(array){
 	}else{
 		var header = `
 		<div class="panel-heading bg-info p-t-10 p-b-10">
-			<span class="pull-left m-t-5 mouse" onclick="homepageRequests()"><img class="lazyload homepageImageTitle" data-src="plugins/images/tabs/ombi.png"> &nbsp; Requests</span>
+			<span class="pull-left m-t-5 mouse homepage-element-title" onclick="homepageRequests()"><img class="lazyload homepageImageTitle" data-src="plugins/images/tabs/ombi.png"> &nbsp; Requests</span>
 			<div class="btn-group pull-right">
 					`+builtDropdown+`
 			</div>
@@ -4770,7 +4774,7 @@ function buildRequestResult(array,media_type=null,list=null,page=null,search=fal
 		<div class="button-box text-center p-b-0">
             <ul class="pagination m-b-0">
                 <li class="`+previousHidden+`"> <a href="javascript:void(0)" onclick="requestList('`+list+`', '`+media_type+`', '`+pagePrevious+`');"><i class="fa fa-angle-left"></i></a> </li>
- 
+
                 `+pageList+`
                 <li class="`+nextHidden+`"> <a href="javascript:void(0)" onclick="requestList('`+list+`', '`+media_type+`', '`+pageNext+`');"><i class="fa fa-angle-right"></i></a> </li>
             </ul>
@@ -5546,6 +5550,7 @@ function buildMetadata(array, source){
 	var genres = '';
 	var actors = '';
 	var rating = '<div class="col-xs-2 p-10"></div>';
+    var sourceIcon = (source === 'jellyfin' && activeInfo.settings.homepage.media.jellyfin) ? 'play' : source;
 	$.each(array.content, function(i,v) {
 		var hasActor = (typeof v.metadata.actors !== 'string') ? true : false;
 		var hasGenre = (typeof v.metadata.genres !== 'string') ? true : false;
@@ -5574,7 +5579,7 @@ function buildMetadata(array, source){
 	                <h2 class="m-b-0 font-medium pull-right text-right">
 						`+v.title+`<button type="button" class="btn bg-org btn-circle close-popup m-l-10"><i class="fa fa-times"></i> </button><br>
 						<small class="m-t-0 text-white">`+v.metadata.tagline+`</small><br>
-						<button class="btn waves-effect waves-light openTab bg-`+source+`" type="button" data-tab-name="`+cleanClass(v.tabName)+`" data-type="`+v.type+`" data-open-tab="`+v.openTab+`" data-url="`+v.address+`" href="javascript:void(0);"> <i class="fa mdi mdi-`+source+` fa-2x"></i> </button>
+						<button class="btn waves-effect waves-light openTab bg-`+source+`" type="button" data-tab-name="`+cleanClass(v.tabName)+`" data-type="`+v.type+`" data-open-tab="`+v.openTab+`" data-url="`+v.address+`" href="javascript:void(0);"> <i class="fa mdi mdi-`+sourceIcon+` fa-2x"></i> </button>
 						`+buildYoutubeLink(v.title+' '+v.metadata.year+' '+v.type)+`
 					</h2>
 	            </div>
@@ -5657,7 +5662,7 @@ function buildHealthChecks(array){
 	<div id="allHealthChecks">
 		<div class="el-element-overlay row">
 		    <div class="col-md-12">
-		        <h4 class="pull-left"><span lang="en">Health Checks</span> : </h4><h4 class="pull-left">&nbsp;<span class="label label-info m-l-20 checkbox-circle good-health-checks mouse">`+checks+`</span></h4>
+		        <h4 class="pull-left homepage-element-title"><span lang="en">Health Checks</span> : </h4><h4 class="pull-left">&nbsp;<span class="label label-info m-l-20 checkbox-circle good-health-checks mouse">`+checks+`</span></h4>
 		        <hr class="hidden-xs">
 		    </div>
 			<div class="clearfix"></div>
@@ -5678,7 +5683,7 @@ function buildUnifi(array){
 	<div id="allUnifi">
 		<div class="row">
 		    <div class="col-md-12">
-		        <h4 class="pull-left"><span lang="en">Unifi</span> : </h4><h4 class="pull-left">&nbsp;</h4>
+		        <h4 class="pull-left homepage-element-title"><span lang="en">Unifi</span> : </h4><h4 class="pull-left">&nbsp;</h4>
 		        <hr class="hidden-xs">
 		    </div>
 			<div class="clearfix"></div>
@@ -5695,10 +5700,11 @@ function buildUnifi(array){
 function buildUnifiItem(array){
     var items = '';
     $.each(array, function(i,v) {
-        console.log(v);
+        //console.log(v);
         var name = (typeof v.subsystem !== 'undefined') ? v.subsystem : '';
         var stats = {};
         var panelColor = '';
+        var proceed = (v.status == 'ok');
         switch (name) {
             case 'wlan':
                 panelColor = 'info';
@@ -5735,37 +5741,38 @@ function buildUnifiItem(array){
             default:
         }
         var statItems = '';
-       console.log(statItems);
-        $.each(stats, function(istat,vstat) {
-            statItems += `
-                <div class="stat-item">
-                    <h6 class="text-uppercase">`+istat+`</h6>
-                    <b>`+vstat+`</b>
-                </div>
-                `;
-        });
-        items += `
-            <!--<div class="col-lg-4 col-md-6">
-                <div class="white-box">
-                    <h3 class="box-title">`+name+`</h3>
-                    <div class="stats-row">
-                        `+statItems+`
+        if(proceed) {
+            $.each(stats, function (istat, vstat) {
+                statItems += `
+                    <div class="stat-item">
+                        <h6 class="text-uppercase">` + istat + `</h6>
+                        <b>` + vstat + `</b>
                     </div>
-                </div>
-            </div>-->
-            <div class="col-lg-4 col-md-6 col-center">
-                <div class="panel panel-`+panelColor+`">
-                    <div class="panel-heading"> <span class="text-uppercase">`+name+`</span>
-                        <div class="pull-right"><a href="#" data-perform="panel-collapse"><i class="ti-minus"></i></a> <a href="#" data-perform="panel-dismiss"><i class="ti-close"></i></a> </div>
+                    `;
+            });
+            items += `
+                <!--<div class="col-lg-4 col-md-6">
+                    <div class="white-box">
+                        <h3 class="box-title">` + name + `</h3>
+                        <div class="stats-row">
+                            ` + statItems + `
+                        </div>
                     </div>
-                    <div class="panel-wrapper collapse in" aria-expanded="true">
-                        <div class="panel-body">
-                           `+statItems+`
+                </div>-->
+                <div class="col-lg-4 col-md-6 col-center">
+                    <div class="panel panel-` + panelColor + `">
+                        <div class="panel-heading"> <span class="text-uppercase">` + name + `</span>
+                            <div class="pull-right"><a href="#" data-perform="panel-collapse"><i class="ti-minus"></i></a> <a href="#" data-perform="panel-dismiss"><i class="ti-close"></i></a> </div>
+                        </div>
+                        <div class="panel-wrapper collapse in" aria-expanded="true">
+                            <div class="panel-body">
+                               ` + statItems + `
+                            </div>
                         </div>
                     </div>
                 </div>
-            </div>
-        `;
+            `;
+        }
     });
     return items;
 }
@@ -5883,7 +5890,7 @@ function homepageUnifi(timeout){
             return false;
         }
         document.getElementById('homepageOrderunifi').innerHTML = '';
-        console.log(response.data);
+        //console.log(response.data);
         if(response.data !== null){
             $('#homepageOrderunifi').html(buildUnifi(response.data));
         }
@@ -6131,7 +6138,7 @@ function getUnifiSite(service, data = ''){
                                 <div class="card-body">
                                     <h4 class="card-title" lang="en">Choose Unifi Site</h4>
                                     `+sites+`
-                                </div>				
+                                </div>
                             </div>
                         </div>
                     </div>
@@ -7277,7 +7284,7 @@ function showLDAPLoginTest(){
                             <div class="form-group mb-0 p-r-10 text-right">
                                 <button type="submit" onclick="testAPIConnection('ldap_login', {'username':$('#ldapUsernameTest').val(),'password':$('#ldapPasswordTest').val()})" class="btn btn-info waves-effect waves-light">Test Login</button>
                             </div>
-                        </div>				
+                        </div>
                     </div>
                 </div>
             </div>

+ 1 - 1
js/langpack/ca[Catalan].json

@@ -290,7 +290,7 @@
         "Choose Media Type": "Trieu el tipus de suport",
         "PHP Version Check": "Verificació de la versió PHP",
         "Don\\'t have an account?": "No tens un compte?",
-        "The value of #987654 is just a placeholder, you can change to any value you like.": "El valor de # 987654 és només un espai reservat, podeu canviar a qualsevol valor que vulgueu.",
+        "The value of #987654 is just a placeholder, you can change to any value you like.": "El valor de #987654 és només un espai reservat, podeu canviar a qualsevol valor que vulgueu.",
         "This is not the same as database authentication - i.e. Plex Authentication | Emby Authentication | FTP Authentication": "Això no és el mateix que l’autenticació de la base de dades: és a dir, l’autenticació de Plex | Autenticació Emby | Autenticació FTP",
         "Status: [ ": "Estat: [ ",
         "This module requires XMLRPC": "Aquest mòdul requereix XMLRPC",

+ 14 - 14
js/langpack/fr[French].json

@@ -263,7 +263,7 @@
         "The Hash Key will be used to decrypt all passwords etc... on the server. {User-Generated]": "La clé de hachage sera utilisée pour déchiffrer tous les mots de passes etc. sur le serveur. [Généré par l'utilisateur]",
         "If using Plex or Emby - It is suggested that you use the username and email of the Admin account.": "Si vous utilisez Plex ou Emby — Il est recommandé d'utiliser le nom d'utilisateur et l'e-mail du compte Administrateur.",
         "Business has Media items hidden [Plex, Emby etc...]": "Professionnel cache des éléments média [Plex, Emby, etc.]",
-        "Personal has everything unlocked - no restrictions": "Personnel ne cache rien — Pas de limites",
+        "Personal has everything unlocked - no restrictions": "Personnel ne cache rien — sans restrictions",
         "Continue To Website": "Continuer vers le site web",
         "Patreon": "Patreon",
         "Cryptos": "Cryptos",
@@ -279,7 +279,7 @@
         "Choose Image": "Choisir l'image",
         "Ping URL": "Envoyer un ping à l'URL",
         "Please set tab as [New Window] on next screen": "Veuillez régler l'onglet sur [Nouvelle fenêtre] sur l'écran suivant",
-        "Tab can be set as iFrame": "L'onglet peut être réglé comme iframe",
+        "Tab can be set as iFrame": "L'onglet peut être réglé comme iFrame",
         "Premier": "Premier",
         "Missing": "Manquant",
         "Unaired": "Non diffusé",
@@ -296,7 +296,7 @@
         "This module requires XMLRPC": "Ce module à besoin de XMLRPC",
         "Misc Options": "Options supplémentaires",
         "Search My Media": "Recherche de média",
-        "Import Plex Users": "Importer les utilisateurs Plex",
+        "Import Plex Users": "Importer des utilisateurs Plex",
         "Import": "Importer",
         "INSTALL": "INSTALLER",
         "INFO": "INFO",
@@ -440,7 +440,7 @@
         "Title": "Titre",
         "Logo": "Logo",
         "Import Users": "Importer des utilisateurs",
-        "Drop files here to upload": "Déposer les fichiers ici pour les envoyer",
+        "Drop files here to upload": "Déposer des fichiers ici pour les envoyer",
         "SpeedTest Settings": "Paramètres du test de vitesse",
         "PHP Mailer Settings": "Paramètres de l'envoi d'e-mails avec PHP",
         "Invites Settings": "Paramètres des invitations",
@@ -481,7 +481,7 @@
         "SMTP Host": "Hôte SMTP",
         "Results For cmd:": "Résultats pour la commande :",
         "Organizr Information:": "Informations sur Organizr",
-        "DB Schema": "Schema BDD",
+        "DB Schema": "Schéma BDD",
         "Misc SSO": "Autre SSO",
         "Tautulli SSO": "Tautulli SSO",
         "Plex SSO": "Plex SSO",
@@ -501,20 +501,20 @@
         "Tab Local URL": "URL Local de l'onglet",
         "PRELOAD": "PRÉCHARGER",
         "Use Ombi Alias Names": "Utiliser les alias Ombi",
-        "TV Show Default Request": "TV Show Default Request",
+        "TV Show Default Request": "Requête par défaut de série TV",
         "Add to Combined Downloader": "Ajouter aux Téléchargements Combinés",
         "http(s)://hostname:port/xmlrpc": " http(s)://hostname:port/xmlrpc",
-        "rTorrent API URL Override": "rTorrent API URL Override",
+        "rTorrent API URL Override": "Redéfinition de l'URL d'API rTorrent",
         "Enable Notify Sounds": "Activer les sons de notifications",
         "Remember Me Length": "Durée du \"Se souvenir de moi\"",
         "Minimum Authentication for Debug Area": "Niveau d'authentification minimum pour la zone de debug",
-        "Account DN": "Account DN",
-        "Bind Username": "Bind Username",
-        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld",
-        "Account Suffix": "Account Suffix",
-        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP",
-        "Account Prefix": "Account Prefix",
-        "LDAP Backend Type": "LDAP Backend Type",
+        "Account DN": "Nom de domaine du compte",
+        "Bind Username": "Associer un nom d'utilisateur",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Suffixe de compte - débute par une virgule - ,ou=people,dc=domain,dc=tld",
+        "Account Suffix": "Suffixe de compte",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Préfixe de compte - i.e. Contrôleur\\ du Contrôleur\\Nom d'utilisateur pour ActiveDirectory - uid= pour OpenLDAP",
+        "Account Prefix": "Préfixe de compte",
+        "LDAP Backend Type": "Type de back-end LDAP",
         "Strict Plex Friends": "Amis Plex Stricts"
     }
 }

+ 5 - 5
js/langpack/nl[Dutch].json

@@ -499,20 +499,20 @@
         "An Error Occured": "Er is een fout opgetreden",
         "Debug Area": "Debug Omgeving",
         "Tab Local URL": "Lokale Tabblad URL",
-        "PRELOAD": "PRELOAD",
+        "PRELOAD": "VOORLADEN",
         "Use Ombi Alias Names": "Gebruik Ombi Alias Namen",
         "TV Show Default Request": "TV Show Standaard Aanvraag",
         "Add to Combined Downloader": "Voeg toe aan Gecombineerde Downloader",
         "http(s)://hostname:port/xmlrpc": "http(s)://hostnaam:poort/xmlrpc",
-        "rTorrent API URL Override": "rTorrent API URL Override",
+        "rTorrent API URL Override": "rTorrent API URL Overschrijven",
         "Enable Notify Sounds": "Zet Notify geluiden aan",
         "Remember Me Length": "Onthoudt Mij Lengte",
         "Minimum Authentication for Debug Area": "Minimum Authenticatie voor Debug Omgeving",
-        "Account DN": "Account DN",
+        "Account DN": "Gebruiker DN",
         "Bind Username": "Koppel Gebruikersnaam",
-        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Gebruiker achtervoegsel - begin met komma",
         "Account Suffix": "Account Achtervoegsel",
-        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Gebruiker voorvoegsel - bijv. Controller\\ van Controller\\Gebruikersnaam voor AD - uid= voor OpenLDAP",
         "Account Prefix": "Account Voorvoegsel",
         "LDAP Backend Type": "LDAP Backend Type",
         "Strict Plex Friends": "Strict Plex Friends"

+ 11 - 11
js/langpack/pl[Polish].json

@@ -21,7 +21,7 @@
         "Password": "Hasło",
         "Sign Up": "Zarejestruj się",
         "Don't have an account?": "Nie masz konta?",
-        "Forgot pwd?": "Zapomniałeś(aś)?",
+        "Forgot pwd?": "Zapomniałeś?",
         "Remember Me": "Zapamiętaj",
         "Login": "Zaloguj się",
         "Installed": "Zainstalowane",
@@ -100,8 +100,8 @@
         "Loading...": "Wczytywanie...",
         "Donate": "Wesprzyj",
         "Edit Group": "Edytuj grupę",
-        "For icons, use the following format:": "Dla ikon, proszę użyć następującego formatu:",
-        "For images, use the following format:": "Dla obrazów, proszę użyć następującego formatu:",
+        "For icons, use the following format:": "Dla ikon, użyj następującego formatu:",
+        "For images, use the following format:": "Dla obrazów, użyj następującego formatu:",
         "You may use an image or icon in this field": "W tym polu, możesz użyć obrazu lub ikony",
         "Image Legend": "Opis obrazu",
         "Category Image": "Obraz kategorii",
@@ -190,7 +190,7 @@
         "To:": "Do:",
         "Email Users": "Użytkownicy e-maila",
         "E-Mail Center": "Centrum e-maili",
-        "You have been invited. Please goto ": "Otrzymałeś(-aś) zaproszenie. Przejdź do",
+        "You have been invited. Please goto ": "Otrzymałeś zaproszenie. Przejdź do ",
         "Use Invite Code": "Użyj kodu zaproszenia",
         "VALID": "WAŻNE",
         "IP ADDRESS": "ADRES IP",
@@ -205,9 +205,9 @@
         "Name or Username": "Imię lub nazwa użytkownika",
         "New Invite": "Nowe zaproszenie",
         "Hover to show ": "Najedź, aby pokazać ",
-        "Parent Directory: ": "Katalog nadrzędny:",
+        "Parent Directory: ": "Katalog nadrzędny: ",
         "The Database will contain sensitive information. Please place in directory outside of root Web Directory.": "Baza zawierać będzie wrażliwe dane. Proszę umieścić ją poza katalogiem strony.",
-        "I Want to Help": "Chciał(a)bym pomóc",
+        "I Want to Help": "Chcę pomóc",
         "Head on over to POEditor and help us translate Organizr into your language": "Przejdź do POEditor i pomóż nam przetłumaczyć Organizr na Twój język",
         "Want to help translate?": "Chcesz pomóc w tłumaczeniu?",
         "Single Sign-On": "Single Sign-On",
@@ -257,12 +257,12 @@
         "Plugin Marketplace": "Sklep z wtyczkami",
         "Marketplace": "Sklep",
         "Chat": "Czat",
-        "Current Directory: ": "Bieżący katalog",
-        "Suggested Directory: ": "Sugerowany katalog",
+        "Current Directory: ": "Bieżący katalog: ",
+        "Suggested Directory: ": "Sugerowany katalog: ",
         "The Registration Password will lockout the registration field with this password. {User-Generated]": "Hasło rejestracyjne będzie blokować pole rejestracyjne. [Generowane przez użytkownika]",
         "The Hash Key will be used to decrypt all passwords etc... on the server. {User-Generated]": "Klucz hash będzie używany do odszyfrowywania wszystkich haseł itp... na serwerze. [Generowane przez użytkownika]",
         "If using Plex or Emby - It is suggested that you use the username and email of the Admin account.": "Jeśli korzystasz z Plex lub Emby - zaleca się użycie nazwy użytkownika i e-maila konta administratora.",
-        "Business has Media items hidden [Plex, Emby etc...]": "Komercyjna posiada ukryte elementy multimediów [Plex, Emby itp...]",
+        "Business has Media items hidden [Plex, Emby etc...]": "Komercyjna posiada ukryte elementy multimediów [Plex, Emby, itp.]",
         "Personal has everything unlocked - no restrictions": "Prywatna posiada wszystko odblokowane - brak ograniczeń",
         "Continue To Website": "Przejdź do strony internetowej",
         "Patreon": "Patreon",
@@ -292,7 +292,7 @@
         "Don\\'t have an account?": "Nie masz konta?",
         "The value of #987654 is just a placeholder, you can change to any value you like.": "Wartość #987654 jest tylko zastępcza i możesz zmienić ją na dowolną inną.",
         "This is not the same as database authentication - i.e. Plex Authentication | Emby Authentication | FTP Authentication": "Nie jest to równoznaczne z uwierzytelnianiem bazy danych - np. Uwierzytelnianie Plex | Uwierzytelnianie Emby | Uwierzytelnianie FTP",
-        "Status: [ ": "Status: [",
+        "Status: [ ": "Status: [ ",
         "This module requires XMLRPC": "Ten moduł wymaga XMLRPC",
         "Misc Options": "Różne opcje",
         "Search My Media": "Wyszukaj multimedia",
@@ -445,7 +445,7 @@
         "PHP Mailer Settings": "Ustawienia PHP Mailer",
         "Invites Settings": "Ustawienia zaproszeń",
         "Chat Settings": "Ustawienia czatu",
-        "Delete ": "Usuń",
+        "Delete ": "Usuń ",
         "App Cluster": "Klaster aplikacji",
         "App ID": "ID aplikacji",
         "API Secret": "Sekretne API",

+ 7 - 0
js/version.json

@@ -215,5 +215,12 @@
     "new": "",
     "fixed": "Plugin Files not loading css files|Remove unifiCookie from unif homepage check",
     "notes": "Please report bugs in GitHub issues page|Updated language translations"
+  },
+  "2.0.362": {
+    "date": "2020-01-13 20:15",
+    "title": "Long awaited release - Sorry!",
+    "new": "Added Jellyfin support - uses same HP Modal as Emby|Added new tab images for Fritzbox, Kodi, OpenHAB and Calibre-Web|Added nginx auth_request dynamic endpoints to allow permission management on organizr front end|Add email header for Auth Proxy use (#1298)|Add another Variable to PR (#1298)|Update local function to take force variable|Added rtorrent bypass cert check",
+    "fixed": "qBittorrent Workaround for v2 API (#1287)|Dependency check fails incorrectly on PHP 7.4 (#1288)|Fix depreciated item on PHP 7.4 (#1288)|7.4 work on array check|XSS Vulnerability (#1291)|Cleaner XSS Vulnerability (#1291)|Closing tabs open up top tab (#1266)|more clean up on unifi homepage item|fix typ-os|Add homepage-element-title class to left-aligned homepage element names|Fix plex show on homepage",
+    "notes": "Please report bugs in GitHub issues page|Updated language translations"
   }
 }

BIN
plugins/images/tabs/calibre-web.png


BIN
plugins/images/tabs/fritzbox.png


BIN
plugins/images/tabs/kodi.png


BIN
plugins/images/tabs/openhab.png


Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác