diff --git a/app/Item.php b/app/Item.php
index 0bc2299c..0067a9a8 100644
--- a/app/Item.php
+++ b/app/Item.php
@@ -57,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,
diff --git a/app/SupportedApps/Transmission.php b/app/SupportedApps/Transmission.php
new file mode 100644
index 00000000..6dfd7077
--- /dev/null
+++ b/app/SupportedApps/Transmission.php
@@ -0,0 +1,166 @@
+ ["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 = '
+
+ - Done'.$completedTorrents.' / '.$torrentCount.'
+ - Down'.format_bytes($rateDownload).'
+ - Up'.format_bytes($rateUpload).'
+
+ ';
+ 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;
+ }
+}
diff --git a/readme.md b/readme.md
index 5b489b14..8b3a55d7 100644
--- a/readme.md
+++ b/readme.md
@@ -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
diff --git a/resources/views/supportedapps/transmission.blade.php b/resources/views/supportedapps/transmission.blade.php
new file mode 100644
index 00000000..be547b54
--- /dev/null
+++ b/resources/views/supportedapps/transmission.blade.php
@@ -0,0 +1,16 @@
+{{ __('app.apps.config') }} ({{ __('app.optional') }})
+
+
+
+
+
+ {!! Form::text('config[username]', null, array('placeholder' => __('app.apps.username'), 'data-config' => 'username', 'class' => 'form-control config-item')) !!}
+
+
+
+ {!! Form::text('config[password]', null, array('placeholder' => __('app.apps.password'), 'data-config' => 'password', 'class' => 'form-control config-item')) !!}
+
+
+
+
+
diff --git a/storage/app/public/supportedapps/transmission.png b/storage/app/public/supportedapps/transmission.png
new file mode 100644
index 00000000..8b55fd4a
Binary files /dev/null and b/storage/app/public/supportedapps/transmission.png differ
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
index 81f7a551..36785cf7 100644
--- a/vendor/composer/autoload_classmap.php
+++ b/vendor/composer/autoload_classmap.php
@@ -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',
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index 98e17593..be408257 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -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',