Browse Source

Merge pull request #1865 from jjohnstondev/prowlarr-homepage

Prowlarr homepage
causefx 3 years ago
parent
commit
6d191b1c8f

+ 12 - 1
api/classes/organizr.class.php

@@ -37,6 +37,7 @@ class Organizr
 	use HTMLHomepageItem;
 	use HTMLHomepageItem;
 	use ICalHomepageItem;
 	use ICalHomepageItem;
 	use JackettHomepageItem;
 	use JackettHomepageItem;
+	use ProwlarrHomepageItem;
 	use JDownloaderHomepageItem;
 	use JDownloaderHomepageItem;
 	use JellyfinHomepageItem;
 	use JellyfinHomepageItem;
 	use LidarrHomepageItem;
 	use LidarrHomepageItem;
@@ -4228,6 +4229,9 @@ class Organizr
 				'jackett' => [
 				'jackett' => [
 					'homepageJackettBackholeDownload' => $this->config['homepageJackettBackholeDownload'] ? true : false
 					'homepageJackettBackholeDownload' => $this->config['homepageJackettBackholeDownload'] ? true : false
 				],
 				],
+				'prowlarr' => [
+					'homepageProwlarrBackholeDownload' => $this->config['homepageProwlarrBackholeDownload'] ? true : false
+				],
 				'options' => [
 				'options' => [
 					'alternateHomepageHeaders' => $this->config['alternateHomepageHeaders'],
 					'alternateHomepageHeaders' => $this->config['alternateHomepageHeaders'],
 					'healthChecksTags' => $this->config['healthChecksTags'],
 					'healthChecksTags' => $this->config['healthChecksTags'],
@@ -4655,6 +4659,13 @@ class Organizr
 						$class .= ' faded';
 						$class .= ' faded';
 					}
 					}
 					break;
 					break;
+				case 'homepageOrderProwlarr':
+					$class = 'bg-inverse';
+					$image = 'plugins/images/tabs/prowlarr.png';
+					if (!$this->config['homepageProwlarrEnabled']) {
+						$class .= ' faded';
+					}
+					break;
 				case 'homepageOrderBookmarks':
 				case 'homepageOrderBookmarks':
 					$class = 'bg-bookmarks';
 					$class = 'bg-bookmarks';
 					$image = 'plugins/images/bookmark.png';
 					$image = 'plugins/images/bookmark.png';
@@ -7799,4 +7810,4 @@ class Organizr
 		return count($request) > 1 ? $results : $results[$firstKey];
 		return count($request) > 1 ? $results : $results[$firstKey];
 	}
 	}
 
 
-}
+}

+ 9 - 0
api/config/default.php

@@ -214,6 +214,14 @@ return [
 	'jackettUseCustomCertificate' => false,
 	'jackettUseCustomCertificate' => false,
 	'jackettDisableCertCheck' => false,
 	'jackettDisableCertCheck' => false,
 	'homepageJackettBackholeDownload' => false,
 	'homepageJackettBackholeDownload' => false,
+	'homepageProwlarrBackholeDownload' => false,
+	'homepageProwlarrEnabled' => false,
+	'homepageProwlarrAuth' => '1',
+	'ProwlarrURL' => '',
+	'ProwlarrToken' => '',
+	'ProwlarrUseCustomCertificate' => false,
+	'ProwlarrDisableCertCheck' => false,
+	'homepageProwlarrBackholeDownload' => false,
 	'homepageCalendarEnabled' => false,
 	'homepageCalendarEnabled' => false,
 	'homepageCalendarAuth' => '4',
 	'homepageCalendarAuth' => '4',
 	'calendariCal' => '',
 	'calendariCal' => '',
@@ -365,6 +373,7 @@ return [
 	'homepageOrderBookmarks' => '40',
 	'homepageOrderBookmarks' => '40',
 	'homepageOrderDonate' => '41',
 	'homepageOrderDonate' => '41',
 	'homepageOrderAdguard' => '42',
 	'homepageOrderAdguard' => '42',
+  'homepageOrderProwlarr' => '43',
 	'homepageShowStreamNames' => false,
 	'homepageShowStreamNames' => false,
 	'homepageShowStreamNamesAuth' => '1',
 	'homepageShowStreamNamesAuth' => '1',
 	'homepageShowStreamNamesWithoutIp' => false,
 	'homepageShowStreamNamesWithoutIp' => false,

+ 185 - 0
api/homepage/prowlarr.php

@@ -0,0 +1,185 @@
+<?php
+
+trait ProwlarrHomepageItem
+{
+	public function prowlarrSettingsArray($infoOnly = false)
+	{
+		$homepageInformation = [
+			'name' => 'Prowlarr',
+			'enabled' => true,
+			'image' => 'plugins/images/tabs/prowlarr.png',
+			'category' => 'Utility',
+			'settingsArray' => __FUNCTION__
+		];
+		if ($infoOnly) {
+			return $homepageInformation;
+		}
+		$homepageSettings = [
+			'debug' => true,
+			'settings' => [
+				'Enable' => [
+					$this->settingsOption('enable', 'homepageProwlarrEnabled'),
+					$this->settingsOption('auth', 'homepageProwlarrAuth'),
+				],
+				'Connection' => [
+					$this->settingsOption('url', 'prowlarrURL'),
+					$this->settingsOption('token', 'prowlarrToken'),
+					$this->settingsOption('disable-cert-check', 'prowlarrDisableCertCheck'),
+					$this->settingsOption('use-custom-certificate', 'prowlarrUseCustomCertificate'),
+				],
+				'Options' => [
+					$this->settingsOption('switch', 'homepageProwlarrBackholeDownload', ['label' => 'Prefer black hole download', 'help' => 'Prefer black hole download link instead of direct/magnet download']),
+				],
+				'Test Connection' => [
+					$this->settingsOption('blank', null, ['label' => 'Please Save before Testing']),
+					$this->settingsOption('test', 'prowlarr'),
+				]
+			]
+		];
+		return array_merge($homepageInformation, $homepageSettings);
+	}
+
+	public function prowlarrHomepagePermissions($key = null)
+	{
+		$permissions = [
+			'main' => [
+				'enabled' => [
+					'homepageProwlarrEnabled'
+				],
+				'auth' => [
+					'homepageProwlarrAuth'
+				],
+				'not_empty' => [
+					'prowlarrURL',
+					'prowlarrToken'
+				]
+			],
+			'test' => [
+				'auth' => [
+					'homepageProwlarrAuth'
+				],
+				'not_empty' => [
+					'prowlarrURL',
+					'prowlarrToken'
+				]
+			]
+		];
+		return $this->homepageCheckKeyPermissions($key, $permissions);
+	}
+
+	public function homepageOrderProwlarr()
+	{
+		if ($this->homepageItemPermissions($this->prowlarrHomepagePermissions('main'))) {
+			return '
+				<div id="' . __FUNCTION__ . '">
+					<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Prowlarr...</h2></div>
+					<script>
+						// Prowlarr
+						homepageProwlarr();
+						// End Prowlarr
+					</script>
+				</div>
+				';
+		}
+	}
+
+	public function testConnectionProwlarr()
+	{
+		if (!$this->homepageItemPermissions($this->prowlarrHomepagePermissions('test'), true)) {
+			return false;
+		}
+		$apiURL = $this->qualifyURL($this->config['prowlarrURL']);
+		$endpoint = $apiURL . '/api/v1/search?apikey=' . $this->config['prowlarrToken'] . '&query=this-is-just-a-test-for-organizr';
+		try {
+			$headers = [];
+			$options = $this->requestOptions($apiURL, 120, $this->config['prowlarrDisableCertCheck'], $this->config['prowlarrUseCustomCertificate']);
+			$response = Requests::get($endpoint, $headers, $options);
+			if ($response->success) {
+				$apiData = json_decode($response->body, true);
+				$api['content'] = $apiData;
+				unset($apiData);
+			} else {
+				$this->setResponse(403, 'Error connecting to Prowlarr');
+				return false;
+			}
+		} catch (Requests_Exception $e) {
+			$this->setResponse(500, $e->getMessage());
+			return false;
+		};
+		$api['content'] = $api['content'] ?? false;
+		$this->setResponse(200, null, $api);
+		return $api;
+	}
+
+	public function searchProwlarrIndexers($query = null)
+	{
+		if (!$this->homepageItemPermissions($this->prowlarrHomepagePermissions('main'), true)) {
+			return false;
+		}
+		if (!$query) {
+			$this->setAPIResponse('error', 'Query was not supplied', 422);
+			return false;
+		}
+		$apiURL = $this->qualifyURL($this->config['prowlarrURL']);
+		$endpoint = $apiURL . '/api/v1/search?apikey=' . $this->config['prowlarrToken'] . '&query=' . urlencode($query);
+		try {
+			$headers = [];
+			$options = $this->requestOptions($apiURL, 120, $this->config['prowlarrDisableCertCheck'], $this->config['prowlarrUseCustomCertificate']);
+			$response = Requests::get($endpoint, $headers, $options);
+			if ($response->success) {
+				$apiData = json_decode($response->body, true);
+				$api['content'] = $apiData;
+				unset($apiData);
+			}
+		} catch (Requests_Exception $e) {
+			$this->setResponse(500, $e->getMessage());
+			return false;
+		};
+		$api['content'] = isset($api['content']) ? $api['content'] : false;
+		$this->setAPIResponse('success', null, 200, $api);
+		return $api;
+	}
+
+	public function performProwlarrBackHoleDownload($guid = null, $indexerId = null)
+	{
+		if (!$this->homepageItemPermissions($this->prowlarrHomepagePermissions('main'), true)) {
+			return false;
+		}
+		if (!$guid) {
+			$this->setAPIResponse('error', 'guid was not supplied', 422);
+			return false;
+		}
+		if (!$indexerId) {
+			$this->setAPIResponse('error', 'indexerId was not supplied', 422);
+			return false;
+		}
+		$apiURL = $this->qualifyURL($this->config['prowlarrURL']);
+		$endpoint = $apiURL . '/api/v1/search?apikey=' . $this->config['prowlarrToken'];
+		try {
+			$headers = [];
+			$data = ['guid'=>$guid,'indexerId'=>$indexerId];
+			$options = $this->requestOptions($apiURL, 120, $this->config['prowlarrDisableCertCheck'], $this->config['prowlarrUseCustomCertificate']);
+			$ch = curl_init($endpoint);
+			$payload = json_encode($data);
+			curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
+			curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
+			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+			$response = curl_exec($ch);
+			curl_close($ch);
+			if ($response) {
+				$api['content'] = $response;
+			}
+		} catch (Requests_Exception $e) {
+			$this->setLoggerChannel('Prowlarr')->error($e);
+			$this->setResponse(500, $e->getMessage());
+			return false;
+		};
+		$api['content'] = isset($api['content']) ? $api['content'] : false;
+		if ($api['content']) {
+			$this->setAPIResponse('success', null, 200, $api);
+		} else {
+			$this->setAPIResponse('error', 'Unknown error', 400, $api);
+		}
+		return $api;
+	}
+}

+ 23 - 1
api/v2/routes/connectionTester.php

@@ -689,6 +689,28 @@ $app->post('/test/jackett', function ($request, $response, $args) {
 		->withHeader('Content-Type', 'application/json;charset=UTF-8')
 		->withHeader('Content-Type', 'application/json;charset=UTF-8')
 		->withStatus($GLOBALS['responseCode']);
 		->withStatus($GLOBALS['responseCode']);
 });
 });
+$app->post('/test/prowlarr', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/prowlarr",
+	 *     summary="Test connection to prowlarr",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionProwlarr();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
 $app->post('/test/slack-logs', function ($request, $response, $args) {
 $app->post('/test/slack-logs', function ($request, $response, $args) {
 	/**
 	/**
 	 * @OA\Post(
 	 * @OA\Post(
@@ -710,4 +732,4 @@ $app->post('/test/slack-logs', function ($request, $response, $args) {
 	return $response
 	return $response
 		->withHeader('Content-Type', 'application/json;charset=UTF-8')
 		->withHeader('Content-Type', 'application/json;charset=UTF-8')
 		->withStatus($GLOBALS['responseCode']);
 		->withStatus($GLOBALS['responseCode']);
-});
+});

+ 18 - 1
api/v2/routes/homepage.php

@@ -523,6 +523,23 @@ $app->post('/homepage/jackett/download/', function ($request, $response, $args)
 		->withHeader('Content-Type', 'application/json;charset=UTF-8')
 		->withHeader('Content-Type', 'application/json;charset=UTF-8')
 		->withStatus($GLOBALS['responseCode']);
 		->withStatus($GLOBALS['responseCode']);
 });
 });
+$app->get('/homepage/prowlarr/{query}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->searchProwlarrIndexers($args['query']);
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->post('/homepage/prowlarr/download/', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$postData = $request->getParsedBody();
+	$Organizr->performProwlarrBackHoleDownload($postData['guid'], $postData['indexerId']);
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
 $app->get('/homepage/trakt/calendar', function ($request, $response, $args) {
 $app->get('/homepage/trakt/calendar', function ($request, $response, $args) {
 	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
 	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
 	$Organizr->getTraktCalendar();
 	$Organizr->getTraktCalendar();
@@ -579,4 +596,4 @@ $app->post('/homepage/donate', function ($request, $response, $args) {
 	return $response
 	return $response
 		->withHeader('Content-Type', 'application/json;charset=UTF-8')
 		->withHeader('Content-Type', 'application/json;charset=UTF-8')
 		->withStatus($GLOBALS['responseCode']);
 		->withStatus($GLOBALS['responseCode']);
-});
+});

+ 166 - 0
js/functions.js

@@ -2464,6 +2464,8 @@ function checkTabHomepageItem(id, name, url, urlLocal){
         addEditHomepageItem(id,'HealthChecks');
         addEditHomepageItem(id,'HealthChecks');
     }else if(name.includes('jackett') || url.includes('jackett') || urlLocal.includes('jackett')){
     }else if(name.includes('jackett') || url.includes('jackett') || urlLocal.includes('jackett')){
 	    addEditHomepageItem(id,'Jackett');
 	    addEditHomepageItem(id,'Jackett');
+    }else if(name.includes('prowlarr') || url.includes('prowlarr') || urlLocal.includes('prowlarr')){
+	    addEditHomepageItem(id,'Prowlarr');
     }else if(name.includes('unifi') || url.includes('unifi') || urlLocal.includes('unifi')){
     }else if(name.includes('unifi') || url.includes('unifi') || urlLocal.includes('unifi')){
 	    addEditHomepageItem(id,'Unifi');
 	    addEditHomepageItem(id,'Unifi');
     }else if(name.includes('tautulli') || url.includes('tautulli') || urlLocal.includes('tautulli')){
     }else if(name.includes('tautulli') || url.includes('tautulli') || urlLocal.includes('tautulli')){
@@ -9934,6 +9936,170 @@ function jackettDownload(url) {
 			OrganizrApiError(xhr, 'Error downloading torrent');
 			OrganizrApiError(xhr, 'Error downloading torrent');
 		});
 		});
 }
 }
+/////////////////////////////////////////////////////////
+function homepageProwlarr(){
+	if(activeInfo.settings.homepage.options.alternateHomepageHeaders){
+		var header = `
+		<div class="col-md-12">
+			<h2 class="text-white m-0 pull-left text-uppercase"><img class="lazyload homepageImageTitle" data-src="plugins/images/tabs/prowlarr.png"> &nbsp; <span lang="en">Prowlarr</span>&nbsp;</h2>
+			<hr class="hidden-xs"><div class="clearfix"></div>
+		</div>
+		<div class="clearfix"></div>
+		<script>$('.prowlarr-panel').removeClass('panel panel-default');</script>
+		`;
+	}else{
+		var header = `
+		<div class="panel-heading bg-info p-t-10 p-b-10">
+			<span class="pull-left m-t-5 text-white"><img class="lazyload homepageImageTitle" data-src="plugins/images/tabs/prowlarr.png" > &nbsp; <span lang="en">Prowlarr</span></span>
+			<div class="clearfix"></div>
+		</div>
+		`;
+	}
+	let html = `
+	<div id="prowlarrSearch" class="row">
+		<div class="col-lg-12">
+			<div class="prowlarr-panel panel panel-default">
+				`+header+`
+				<div class="panel-wrapper p-b-0 collapse in">
+					<div class="white-box">
+	                    <h3 class="box-title m-b-0" lang="en">Search</h3>
+	                    
+	                    <form onsubmit="searchProwlarr();return false;">
+	                        <div class="input-group m-b-30">
+	                        	<span class="input-group-btn hidden">
+									<button type="button" class="btn waves-effect waves-light btn-primary clearProwlarr" onclick="clearProwlarr();"><i class="fa fa-eraser"></i></button>
+								</span>
+	                            <input id="prowlarr-search-query" class="form-control" placeholder="Search for..." lang="en">
+	                            <span class="input-group-btn">
+									<button type="submit" class="btn waves-effect waves-light btn-info"><i class="fa fa-search"></i></button>
+								</span>
+	                        </div>
+	
+	                    </form>
+	                    
+	                    <div class="prowlarrDataTable hidden">
+        					<h3 class="box-title m-b-0" lang="en">Results</h3>
+					        <div class="table-responsive">
+					            <table id="prowlarrDataTable" class="table table-striped">
+					                <thead>
+					                    <tr>
+					                        <th lang="en">Date</th>
+					                        <th lang="en">Indexer</th>
+					                        <th lang="en">Title</th>
+					                        <th lang="en">Size</th>
+					                        <th lang="en">Grabs</th>
+					                        <th lang="en">Seeders</th>
+					                        <th lang="en">Leechers</th>
+					                        <th lang="en">Download</th>
+					                    </tr>
+					                </thead>
+					                <tbody></tbody>
+					            </table>
+					        </div>
+    					</div>
+	                </div>
+					
+				</div>
+			</div>
+		</div>
+	</div>
+	`;
+	$('#homepageOrderProwlarr').html(html);
+}
+function clearProwlarr(){
+	$('#prowlarr-search-query').val('');
+	$('.clearProwlarr').parent().addClass('hidden');
+	$('#prowlarrDataTable').DataTable().destroy();
+	$('.prowlarrDataTable').addClass('hidden');
+}
+function searchProwlarr(){
+	let query = $('#prowlarr-search-query').val();
+	if(query !== ''){
+		$('.prowlarrDataTable').removeClass('hidden');
+		ajaxloader('#prowlarrSearch .panel-wrapper', 'in');
+	}else{
+		return false;
+	}
+	$.fn.dataTable.ext.errMode = 'none';
+	$('#prowlarrDataTable').DataTable().destroy();
+	let preferBlackholeDownload = activeInfo.settings.homepage.prowlarr.homepageProwlarrBackholeDownload
+	let prowlarrTable = $("#prowlarrDataTable")
+		.on( 'error.dt', function ( e, settings, techNote, message ) {
+			console.log( 'An error has been reported by DataTables: ', message );
+		} )
+		.DataTable( {
+			"ajax": {
+				"url": "api/v2/homepage/prowlarr/" + query,
+				"dataSrc": function ( json ) {
+					return json.response.data.content;
+				}
+			},
+			"columns": [
+				{ data: 'publishDate',
+					render: function ( data, type, row ) {
+						if ( type === 'display' || type === 'filter' ) {
+							var m = moment.tz(data, activeInfo.timezone);
+							return moment.utc(m, "YYYY-MM-DD hh:mm[Z]").local().fromNow();
+						}
+						return data;
+					}
+				},
+				{ data: 'indexer' },
+				{ data: 'title',
+					render: function ( data, type, row ) {
+						if(row.Details !== null){
+							return '<a href="'+row['infoUrl']+'" target="_blank">'+data+'</a>';
+						}else{
+							return data;
+						}
+
+					}
+				},
+				{ data: 'size',	render: function ( data ) {
+                    return humanFileSize(data, false);
+                }},
+				{ data: 'grabs' },
+				{ data: 'seeders' },
+				{ data: 'leechers' },
+				{ data: 'downloadUrl',
+					render: function ( data, type, row ) {
+						if ( type === 'display' || type === 'filter' ) {
+            	if(data !== null){
+								if(preferBlackholeDownload === true && row.guid !== null){
+									return '<a onclick="prowlarrDownload(\''+row.guid+","+row.indexerId+'\');return false;" href="#"><i class="fa fa-cloud-download"></i></a>';
+								} else {
+									return '<a href="'+data+'" target="_blank"><i class="fa fa-download"></i></a>';
+								}
+							}	else{
+								return 'No Download Link';
+							}
+						}
+						return data;
+					}
+				},
+			],
+			"order": [[ 5, 'desc' ]],
+			"initComplete": function(settings, json) {
+				ajaxloader();
+				//ajaxblocker('.prowlarr-panel .white-box');
+				$('.clearProwlarr').parent().removeClass('hidden');
+			}
+		} );
+}
+function prowlarrDownload(url) {
+	const args = url.split(",")
+	var post = {
+		guid: args[0],
+		indexerId: args[1],
+	};
+	organizrAPI2('POST', 'api/v2/homepage/prowlarr/download/', post, true)
+		.success(function() {
+			message('Torrent downloaded','',activeInfo.settings.notifications.position,"#FFF","success","5000");
+		})
+		.fail(function(xhr) {
+			OrganizrApiError(xhr, 'Error downloading torrent');
+		});
+}
 function homepageOctoprint(timeout){
 function homepageOctoprint(timeout){
     var timeout = (typeof timeout !== 'undefined') ? timeout : activeInfo.settings.homepage.refresh.homepageOctoprintRefresh;
     var timeout = (typeof timeout !== 'undefined') ? timeout : activeInfo.settings.homepage.refresh.homepageOctoprintRefresh;
     organizrAPI2('GET','api/v2/homepage/octoprint/data').success(function(data) {
     organizrAPI2('GET','api/v2/homepage/octoprint/data').success(function(data) {