Compare commits

...

9 Commits
1.4.6 ... 1.4.7

Author SHA1 Message Date
KodeStar
1a8e2b92de closes #128 2018-03-11 17:47:05 +00:00
KodeStar
cf63e751bf Merge pull request #127 from albertsj1/add_transmission
Add Enhanced App Transmission
2018-03-11 17:41:02 +00:00
KodeStar
0c51bc2771 fix multiple tags 2018-03-11 17:40:08 +00:00
John Alberts
5f278cce3e cleanup a couple symlinks I didn't intend to commit and some errant whitespace 2018-03-09 07:59:01 -08:00
KodeStar
f8cf3ac832 Merge pull request #125 from frenos/foundation_glances
Foundation App for Glances
2018-03-09 08:05:07 +00:00
John Alberts
50bdd02a72 Add Enhanced App Transmission 2018-03-08 16:18:39 -08:00
frenos
424155e5cd Foundation App for Glances
Default color is the backgroundcolor of the official site.
2018-03-07 17:39:45 +01:00
Chris
c4a4d25f7e change js version to break cache 2018-03-06 09:43:54 +00:00
KodeStar
ca2e135cba closes #122, closes #82, 2018-03-05 19:38:23 +00:00
26 changed files with 285 additions and 41 deletions

View File

@@ -31,6 +31,7 @@ class Item extends Model
'Duplicati' => \App\SupportedApps\Duplicati::class,
'Emby' => \App\SupportedApps\Emby::class,
'Gitea' => \App\SupportedApps\Gitea::class,
'Glances' => \App\SupportedApps\Glances::class,
'Graylog' => \App\SupportedApps\Graylog::class,
'Home Assistant' => \App\SupportedApps\HomeAssistant::class,
'Jackett' => \App\SupportedApps\Jackett::class,
@@ -56,6 +57,7 @@ class Item extends Model
'Sabnzbd' => \App\SupportedApps\Sabnzbd::class,
'Sickrage' => \App\SupportedApps\Sickrage::class,
'Sonarr' => \App\SupportedApps\Sonarr::class,
'Transmission' => \App\SupportedApps\Transmission::class,
'Traefik' => \App\SupportedApps\Traefik::class,
'Ttrss' => \App\SupportedApps\Ttrss::class,
'UniFi' => \App\SupportedApps\Unifi::class,

View File

@@ -0,0 +1,14 @@
<?php namespace App\SupportedApps;
class Glances implements Contracts\Applications {
public function defaultColour()
{
return '#2c363f';
}
public function icon()
{
return 'supportedapps/glances.png';
}
}

View File

@@ -39,25 +39,27 @@ class Nzbget implements Contracts\Applications, Contracts\Livestats {
}
public function executeConfig()
{
$output = '';
$html = '';
$active = 'inactive';
$res = $this->buildRequest('status');
$data = json_decode($res->getBody());
//$data->result->RemainingSizeMB = '10000000';
//$data->result->DownloadRate = '100000000';
$size = $data->result->RemainingSizeMB;
$rate = $data->result->DownloadRate;
$queue_size = format_bytes($size*1000*1000, false, ' <span>', '</span>');
$current_speed = format_bytes($rate, false, ' <span>');
if($data) {
$size = $data->result->RemainingSizeMB;
$rate = $data->result->DownloadRate;
$queue_size = format_bytes($size*1000*1000, false, ' <span>', '</span>');
$current_speed = format_bytes($rate, false, ' <span>');
if($size > 0 || $rate > 0) {
$output = '
$active = ($size > 0 || $rate > 0) ? 'active' : 'inactive';
$html = '
<ul class="livestats">
<li><span class="title">Queue</span><strong>'.$queue_size.'</strong></li>
<li><span class="title">Speed</span><strong>'.$current_speed.'/s</span></strong></li>
</ul>
';
}
return $output;
return json_encode(['status' => $active, 'html' => $html]);
}
public function buildRequest($endpoint)
{
@@ -73,7 +75,7 @@ class Nzbget implements Contracts\Applications, Contracts\Livestats {
$api_url = $rebuild_url.'/jsonrpc/'.$endpoint;
$client = new Client(['http_errors' => false]);
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;

View File

@@ -39,17 +39,18 @@ class Pihole implements Contracts\Applications, Contracts\Livestats {
public function executeConfig()
{
$output = '';
$html = '';
$active = 'active';
$res = $this->buildRequest();
$data = json_decode($res->getBody());
$output = '
$html = '
<ul class="livestats">
<li><span class="title">Domains<br />Blocked</span><strong>'.$data->domains_being_blocked.'</strong></li>
<li><span class="title">Blocked<br />Today</span><strong>'.$data->ads_blocked_today.'</span></strong></li>
</ul>
';
return $output;
return json_encode(['status' => $active, 'html' => $html]);
}
public function buildRequest()
@@ -62,7 +63,7 @@ class Pihole implements Contracts\Applications, Contracts\Livestats {
$api_url = $url.'/api.php';
//die( $api_url.' --- ');
$client = new Client(['http_errors' => false]);
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;

View File

@@ -44,18 +44,19 @@ class Plexpy implements Contracts\Applications, Contracts\Livestats {
}
public function executeConfig()
{
$output = '';
$html = '';
$active = 'active';
$res = $this->buildRequest('get_activity');
$data = json_decode($res->getBody());
$stream_count = $data->response->data->stream_count;
$output = '
$html = '
<ul class="livestats">
<li><span class="title">Stream Count</span><strong>'.$stream_count.'</strong></li>
</ul>
';
return $output;
return json_encode(['status' => $active, 'html' => $html]);
}
public function buildRequest($endpoint)
{
@@ -67,7 +68,7 @@ class Plexpy implements Contracts\Applications, Contracts\Livestats {
$api_url = $url.'/api/v2?apikey='.$apikey.'&cmd='.$endpoint;
$client = new Client(['http_errors' => false]);
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;

View File

@@ -40,6 +40,7 @@ class Runeaudio implements Contracts\Applications, Contracts\Livestats {
public function executeConfig()
{
$output = '';
$active = 'active';
$artist = '';
$song_title = '';
$res = $this->buildRequest('currentsong');
@@ -71,7 +72,7 @@ class Runeaudio implements Contracts\Applications, Contracts\Livestats {
$output = $output.'</ul>';
return $output;
return json_encode(['status' => $active, 'html' => $output]);
}
public function buildRequest($endpoint)
@@ -84,7 +85,7 @@ class Runeaudio implements Contracts\Applications, Contracts\Livestats {
$api_url = $url.'/command/?cmd='.$endpoint;
//die( $api_url.' --- ');
$client = new Client(['http_errors' => false]);
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;

View File

@@ -44,25 +44,27 @@ class Sabnzbd implements Contracts\Applications, Contracts\Livestats {
}
public function executeConfig()
{
$output = '';
$html = '';
$active = 'inactive';
$res = $this->buildRequest('queue');
$data = json_decode($res->getBody());
//$data->result->RemainingSizeMB = '10000000';
//$data->result->DownloadRate = '100000000';
$size = $data->queue->mbleft;
$rate = $data->queue->kbpersec;
$queue_size = format_bytes($size*1000*1000, false, ' <span>', '</span>');
$current_speed = format_bytes($rate*1000, false, ' <span>');
if($data) {
$size = $data->queue->mbleft;
$rate = $data->queue->kbpersec;
$queue_size = format_bytes($size*1000*1000, false, ' <span>', '</span>');
$current_speed = format_bytes($rate*1000, false, ' <span>');
if($size > 0 || $rate > 0) {
$output = '
<ul class="livestats">
<li><span class="title">Queue</span><strong>'.$queue_size.'</strong></li>
<li><span class="title">Speed</span><strong>'.$current_speed.'/s</span></strong></li>
</ul>
$active = ($size > 0 || $rate > 0) ? 'active' : 'inactive';
$html = '
<ul class="livestats">
<li><span class="title">Queue</span><strong>'.$queue_size.'</strong></li>
<li><span class="title">Speed</span><strong>'.$current_speed.'/s</span></strong></li>
</ul>
';
}
return $output;
return json_encode(['status' => $active, 'html' => $html]);
}
public function buildRequest($endpoint)
{
@@ -70,12 +72,15 @@ class Sabnzbd implements Contracts\Applications, Contracts\Livestats {
$url = $config->url;
$apikey = $config->apikey;
//print_r($config);
//die();
$url = rtrim($url, '/');
$api_url = $url.'/api?output=json&apikey='.$apikey.'&mode='.$endpoint;
//die( $api_url.' --- ');
$client = new Client(['http_errors' => false]);
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;

View File

@@ -0,0 +1,166 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;
class Transmission implements Contracts\Applications, Contracts\Livestats
{
private $_client;
private $_clientOptions = array();
public function __construct()
{
$body = array();
$body["method"] = "torrent-get";
$body["arguments"] = array("fields" => ["percentDone","status","rateDownload","rateUpload"]);
$this->_client = new Client(
['http_errors' => false,
'timeout' => 10,
'body' => json_encode($body)]
);
}
public function defaultColour()
{
return '#950003';
}
public function icon()
{
return 'supportedapps/Transmission.png';
}
public function configDetails()
{
return 'transmission';
}
public function testConfig()
{
$res = $this->sendRequest();
if ($res == null) {
echo 'Transmission connection failed';
return;
}
switch($res->getStatusCode()) {
case 200:
$data = json_decode($res->getBody());
echo "Successfully connected with status: ".$data->result."\n";
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and includes the port';
break;
case 409:
echo 'Failed: Incorrect session id';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'active';
$res = $this->sendRequest();
if ($res == null) {
Log::debug('Transmission connection failed');
return '';
}
$data = json_decode($res->getBody());
if (! isset($data->arguments)) {
Log::debug('Failed to fetch data from Transmission');
return '';
}
$torrents = $data->arguments->torrents;
$torrentCount = count($torrents);
$rateDownload = $rateUpload = $completedTorrents = 0;
foreach ($torrents as $thisTorrent) {
$rateDownload += $thisTorrent->rateDownload;
$rateUpload += $thisTorrent->rateUpload;
if ($thisTorrent->percentDone == 1) {
$completedTorrents += 1;
}
}
if ($torrentCount - $completedTorrents == 0) {
// Don't poll as frequently if we don't have any active torrents
$active = 'inactive';
}
$html = '
<ul class="livestats">
<li><span class="title">Done</span><sub>'.$completedTorrents.' / '.$torrentCount.'</sub></li>
<li><span class="title">Down</span><sub>'.format_bytes($rateDownload).'</sub></li>
<li><span class="title">Up</span><sub>'.format_bytes($rateUpload).'</sub></li>
</ul>
';
return json_encode(['status' => $active, 'html' => $html]);;
}
private function sendRequest()
{
$optionsSet = $this->setClientOptions();
if (! $optionsSet) {
// Pass the failed response back up the chain
return null;
}
$res = $this->torrentGet();
if ($res->getStatusCode() == 409) {
$this->setClientOptions();
$res = $this->torrentGet();
}
return $res;
}
private function torrentGet()
{
$res = null;
try{
$res = $this->_client->request(
'POST',
$this->getApiUrl(),
$this->_clientOptions
);
}catch(\GuzzleHttp\Exception\BadResponseException $e){
Log::error("Connection to {$e->getRequest()->getUrl()} failed");
Log::debug($e->getMessage());
$res = $e->getRequest();
}catch(\GuzzleHttp\Exception\ConnectException $e) {
Log::error("Transmission connection refused");
Log::debug($e->getMessage());
}
return $res;
}
private function setClientOptions()
{
if ($this->config->username != '' || $this->config->password != '') {
$this->_clientOptions = ['auth'=> [$this->config->username, $this->config->password, 'Basic']];
}
try{
$res = $this->_client->request('HEAD', $this->getApiUrl(), $this->_clientOptions);
$xtId = $res->getHeaderLine('X-Transmission-Session-Id');
if ($xtId != null) {
$this->_clientOptions['headers'] = ['X-Transmission-Session-Id' => $xtId];
} else {
Log::error("Unable to get Transmission session information");
Log::debug("Status Code: ".$res->getStatusCode());
}
}catch(\GuzzleHttp\Exception\ConnectException $e){
Log::error("Failed connection to Transmission");
return false;
}
return true;
}
private function getApiUrl()
{
$url = $this->config->url;
$url = rtrim($url, '/');
$apiUrl = $url.'/transmission/rpc';
return $apiUrl;
}
}

View File

@@ -14,7 +14,7 @@ return [
*/
'name' => env('APP_NAME', 'Heimdall'),
'version' => '1.4.6',
'version' => '1.4.7',
/*
|--------------------------------------------------------------------------

7
public/js/app.js vendored
View File

@@ -28,9 +28,10 @@ $.when( $.ready ).then(function() {
(function worker() {
$.ajax({
url: '/get_stats/'+id,
dataType: 'json',
success: function(data) {
container.html(data);
if(data != '') timer = increaseby;
container.html(data.html);
if(data.status == 'active') timer = increaseby;
else {
if(timer < max_timer) timer += 2000;
}
@@ -130,7 +131,7 @@ $.when( $.ready ).then(function() {
var apiurl = $('#create input[name=url]').val();
var override_url = $('#create input[name=override_url]');
var override_url = $('#override_url');
if(override_url.length && override_url.val() != '') {
apiurl = override_url;
}

View File

@@ -31,6 +31,7 @@ You can use the app to link to any site or application, but Foundation apps will
- NZBGet
- Pihole
- Sabnzbd
- Transmission
**Foundation**
- Deluge

View File

@@ -19,9 +19,10 @@ $.when( $.ready ).then(function() {
(function worker() {
$.ajax({
url: '/get_stats/'+id,
dataType: 'json',
success: function(data) {
container.html(data);
if(data != '') timer = increaseby;
container.html(data.html);
if(data.status == 'active') timer = increaseby;
else {
if(timer < max_timer) timer += 2000;
}
@@ -121,7 +122,7 @@ $.when( $.ready ).then(function() {
var apiurl = $('#create input[name=url]').val();
var override_url = $('#create input[name=override_url]');
var override_url = $('#override_url');
if(override_url.length && override_url.val() != '') {
apiurl = override_url;
}

View File

@@ -65,6 +65,7 @@ return [
'apps.add_tag' => 'Add tag',
'apps.tag_name' => 'Tag name',
'apps.tags' => 'Tags',
'apps.override' => 'If different to main url',
'url' => 'Url',
'title' => 'Title',

View File

@@ -88,7 +88,7 @@
<script src="//ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>!window.jQuery && document.write('<script src="/js/jquery-3.3.1.min.js"><\/script>')</script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="/js/app.js"></script>
<script src="/js/app.js?v=2"></script>
@yield('scripts')
</body>

View File

@@ -34,7 +34,7 @@
{!! Form::text('colour', null, array('placeholder' => __('app.apps.hex'),'class' => 'form-control color-picker')) !!}
<hr />
<label>{{ __('app.apps.tags') }} ({{ __('app.optional') }})</label>
{!! Form::select('tags', $tags, $current_tags, ['class' => 'tags', 'multiple']) !!}
{!! Form::select('tags[]', $tags, $current_tags, ['class' => 'tags', 'multiple']) !!}
</div>
<div class="input">
<label>{{ __('app.apps.icon') }}</label>

View File

@@ -1,6 +1,11 @@
<h2>{{ __('app.apps.config') }} ({{ __('app.optional') }})</h2>
<div class="items">
<input type="hidden" data-config="type" class="config-item" name="config[type]" value="\App\SupportedApps\Nzbget" />
<div class="input">
<label>{{ strtoupper(__('app.url')) }}</label>
{!! Form::text('config[override_url]', null, array('placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control')) !!}
</div>
<div class="input">
<label>{{ __('app.apps.username') }}</label>
{!! Form::text('config[username]', null, array('placeholder' => __('app.apps.username'), 'data-config' => 'username', 'class' => 'form-control config-item')) !!}

View File

@@ -2,6 +2,11 @@
<div class="items">
<input type="hidden" data-config="type" class="config-item" name="config[type]" value="\App\SupportedApps\Pihole" />
<input type="hidden" data-config="dataonly" class="config-item" name="config[dataonly]" value="1" />
<div class="input">
<label>{{ strtoupper(__('app.url')) }}</label>
{!! Form::text('config[override_url]', null, array('placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control')) !!}
</div>
<div class="input">
<label>{{ __('app.apps.enable') }}</label>
{!! Form::hidden('config[enabled]', '0') !!}

View File

@@ -1,6 +1,10 @@
<h2>{{ __('app.apps.config') }} ({{ __('app.optional') }})</h2>
<div class="items">
<input type="hidden" data-config="type" class="config-item" name="config[type]" value="\App\SupportedApps\Plexpy" />
<div class="input">
<label>{{ strtoupper(__('app.url')) }}</label>
{!! Form::text('config[override_url]', null, array('placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control')) !!}
</div>
<div class="input">
<label>{{ __('app.apps.apikey') }}</label>
{!! Form::text('config[apikey]', null, array('placeholder' => __('app.apps.apikey'), 'data-config' => 'apikey', 'class' => 'form-control config-item')) !!}

View File

@@ -1,6 +1,11 @@
<h2>{{ __('app.apps.config') }} ({{ __('app.optional') }})</h2>
<div class="items">
<input type="hidden" data-config="type" class="config-item" name="config[type]" value="\App\SupportedApps\Proxmox" />
<div class="input">
<label>{{ strtoupper(__('app.url')) }}</label>
{!! Form::text('config[override_url]', null, array('placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control')) !!}
</div>
<div class="input">
<label>{{ __('app.apps.username') }}</label>
{!! Form::text('config[username]', null, array('placeholder' => __('app.apps.username'), 'data-config' => 'username', 'class' => 'form-control config-item')) !!}

View File

@@ -2,6 +2,11 @@
<div class="items">
<input type="hidden" data-config="type" class="config-item" name="config[type]" value="\App\SupportedApps\Runeaudio" />
<input type="hidden" data-config="dataonly" class="config-item" name="config[dataonly]" value="1" />
<div class="input">
<label>{{ strtoupper(__('app.url')) }}</label>
{!! Form::text('config[override_url]', null, array('placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control')) !!}
</div>
<div class="input">
<label>{{ __('app.apps.enable') }}</label>
{!! Form::hidden('config[enabled]', '0') !!}

View File

@@ -1,6 +1,12 @@
<h2>{{ __('app.apps.config') }} ({{ __('app.optional') }})</h2>
<div class="items">
<input type="hidden" data-config="type" class="config-item" name="config[type]" value="\App\SupportedApps\Sabnzbd" />
<div class="input">
<label>{{ strtoupper(__('app.url')) }}</label>
{!! Form::text('config[override_url]', null, array('placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control')) !!}
</div>
<div class="input">
<label>{{ __('app.apps.apikey') }}</label>
{!! Form::text('config[apikey]', null, array('placeholder' => __('app.apps.apikey'), 'data-config' => 'apikey', 'class' => 'form-control config-item')) !!}

View File

@@ -0,0 +1,16 @@
<h2>{{ __('app.apps.config') }} ({{ __('app.optional') }})</h2>
<div class="items">
<input type="hidden" name="config[enabled]" value="1" />
<input type="hidden" data-config="type" class="config-item" name="config[type]" value="\App\SupportedApps\Transmission" />
<div class="input">
<label>{{ __('app.apps.username') }}</label>
{!! Form::text('config[username]', null, array('placeholder' => __('app.apps.username'), 'data-config' => 'username', 'class' => 'form-control config-item')) !!}
</div>
<div class="input">
<label>{{ __('app.apps.password') }}</label>
{!! Form::text('config[password]', null, array('placeholder' => __('app.apps.password'), 'data-config' => 'password', 'class' => 'form-control config-item')) !!}
</div>
<div class="input">
<button style="margin-top: 32px;" class="btn test" id="test_config">Test</button>
</div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -58,6 +58,7 @@ return array(
'App\\SupportedApps\\Sabnzbd' => $baseDir . '/app/SupportedApps/Sabnzbd.php',
'App\\SupportedApps\\Sonarr' => $baseDir . '/app/SupportedApps/Sonarr.php',
'App\\SupportedApps\\Traefik' => $baseDir . '/app/SupportedApps/Traefik.php',
'App\\SupportedApps\\Transmission' => $baseDir . '/app/SupportedApps/Transmission.php',
'App\\SupportedApps\\Ttrss' => $baseDir . '/app/SupportedApps/Ttrss.php',
'App\\SupportedApps\\Unifi' => $baseDir . '/app/SupportedApps/Unifi.php',
'App\\SupportedApps\\ruTorrent' => $baseDir . '/app/SupportedApps/ruTorrent.php',

View File

@@ -396,6 +396,7 @@ class ComposerStaticInit4b6fb9210a1ea37c2db27b8ff53a1ecf
'App\\SupportedApps\\Sabnzbd' => __DIR__ . '/../..' . '/app/SupportedApps/Sabnzbd.php',
'App\\SupportedApps\\Sonarr' => __DIR__ . '/../..' . '/app/SupportedApps/Sonarr.php',
'App\\SupportedApps\\Traefik' => __DIR__ . '/../..' . '/app/SupportedApps/Traefik.php',
'App\\SupportedApps\\Transmission' => __DIR__ . '/../..' . '/app/SupportedApps/Transmission.php',
'App\\SupportedApps\\Ttrss' => __DIR__ . '/../..' . '/app/SupportedApps/Ttrss.php',
'App\\SupportedApps\\Unifi' => __DIR__ . '/../..' . '/app/SupportedApps/Unifi.php',
'App\\SupportedApps\\ruTorrent' => __DIR__ . '/../..' . '/app/SupportedApps/ruTorrent.php',