From 47e270161b93d77df5936bd87da0d41f38929c8c Mon Sep 17 00:00:00 2001 From: Joseph Date: Thu, 5 Aug 2021 13:25:22 +0100 Subject: [PATCH 1/6] [SoundcloudBridge] Add support for albums, reposts & likes --- bridges/SoundcloudBridge.php | 95 ++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 32 deletions(-) diff --git a/bridges/SoundcloudBridge.php b/bridges/SoundcloudBridge.php index fe1e9414c37..835119e1edc 100644 --- a/bridges/SoundcloudBridge.php +++ b/bridges/SoundcloudBridge.php @@ -1,28 +1,35 @@ array( 'name' => 'username', 'required' => true ), 't' => array( - 'name' => 'type', + 'name' => 'Content', 'type' => 'list', 'defaultValue' => 'tracks', 'values' => array( + 'All' => 'all', 'Tracks' => 'tracks', - 'Playlists' => 'playlists' + 'Albums' => 'albums', + 'Playlists' => 'playlists', + 'Reposts' => 'reposts', + 'Likes' => 'likes' ) ) )); + private $apiUrl = 'https://api-v2.soundcloud.com/'; + private $playerUrl = 'https://w.soundcloud.com/player/?url=http'; + private $widgetUrl = 'https://widget.sndcdn.com/'; + private $feedTitle = null; private $feedIcon = null; private $clientIDCache = null; @@ -30,35 +37,31 @@ class SoundCloudBridge extends BridgeAbstract { private $clientIdRegex = '/client_id.*?"(.+?)"/'; private $widgetRegex = '/widget-.+?\.js/'; - public function collectData(){ - $res = $this->apiGet('resolve', array( - 'url' => 'https://soundcloud.com/' . $this->getInput('u') - )) or returnServerError('No results for this query'); + public function collectData() { + $res = $this->getUser($this->getInput('u')) + or returnServerError('No results for this query'); $this->feedTitle = $res->username; $this->feedIcon = $res->avatar_url; - $tracks = $this->apiGet( - 'users/' . urlencode($res->id) . '/' . $this->getInput('t'), - array('limit' => 31) - ) or returnServerError('No results for this user/playlist'); + $tracks = $this->getUserItems($res->id, $this->getInput('t')) + or returnServerError('No results for ' . $this->getInput('t')); + + $hasTrackObject = array('all', 'reposts', 'likes'); foreach ($tracks->collection as $index => $track) { + if (in_array($this->getInput('t'), $hasTrackObject) === true) { + $track = $track->track; + } + $item = array(); $item['author'] = $track->user->username; $item['title'] = $track->user->username . ' - ' . $track->title; $item['timestamp'] = strtotime($track->created_at); $item['content'] = nl2br($track->description); $item['enclosures'][] = $track->artwork_url; - - $item['id'] = self::URI - . urlencode($this->getInput('u')) - . '/' - . urlencode($track->permalink); - $item['uri'] = self::URI - . urlencode($this->getInput('u')) - . '/' - . urlencode($track->permalink); + $item['id'] = $track->permalink_url; + $item['uri'] = $track->permalink_url; $this->items[] = $item; if (count($this->items) >= 10) { @@ -75,13 +78,17 @@ public function getIcon(){ return parent::getIcon(); } - public function getURI(){ - return 'https://soundcloud.com/' . $this->getInput('u'); + public function getURI() { + if ($this->getInput('u')) { + return self::URI . $this->getInput('u') . '/' . $this->getInput('t'); + } + + return parent::getURI(); } - public function getName(){ + public function getName() { if($this->feedTitle) { - return $this->feedTitle . ' - ' . self::NAME; + return $this->feedTitle . ' - ' . ucfirst($this->getInput('t')) . ' - ' . self::NAME; } return parent::getName(); @@ -114,7 +121,7 @@ private function refreshClientID(){ $this->initClientIDCache(); // Without url=http, this returns a 404 - $playerHTML = getContents('https://w.soundcloud.com/player/?url=http') + $playerHTML = getContents($this->playerUrl) or returnServerError('Unable to get player page.'); // Extract widget JS filenames from player page @@ -125,7 +132,7 @@ private function refreshClientID(){ // Loop widget js files and extract client ID foreach ($matches[0] as $widgetFile) { - $widgetURL = 'https://widget.sndcdn.com/' . $widgetFile; + $widgetURL = $this->widgetUrl . $widgetFile; $widgetJS = getContents($widgetURL) or returnServerError('Unable to get widget JS page.'); @@ -143,22 +150,46 @@ private function refreshClientID(){ } } - private function buildAPIURL($endpoint, $parameters){ - return 'https://api-v2.soundcloud.com/' + private function buildApiUrl($endpoint, $parameters) { + return $this->apiUrl . $endpoint . '?' . http_build_query($parameters); } - private function apiGet($endpoint, $parameters = array()) { + private function getUser($username) { + $parameters = array('url' => self::URI . $username); + + return $this->getApi('resolve', $parameters); + } + + private function getUserItems($userId, $type) { + $parameters = array('limit' => 10); + $endpoint = 'users/' . $userId . '/' . $type; + + if ($type === 'all') { + $endpoint = 'stream/users/' . $userId; + } + + if ($type === 'reposts') { + $endpoint = 'stream/users/' . $userId . '/' . $type; + } + + return $this->getApi($endpoint, $parameters); + } + + private function getApi($endpoint, $parameters) { $parameters['client_id'] = $this->getClientID(); + $url = $this->buildApiUrl($endpoint, $parameters); try { - return json_decode(getContents($this->buildAPIURL($endpoint, $parameters))); + return json_decode(getContents($url)); } catch (Exception $e) { // Retry once with refreshed client ID $parameters['client_id'] = $this->refreshClientID(); - return json_decode(getContents($this->buildAPIURL($endpoint, $parameters))); + $url = $this->buildApiUrl($endpoint, $parameters); + + return json_decode(getContents($url)); } } } From b204e80f476e90d7dd85524a2bc1b4dea8e0ae78 Mon Sep 17 00:00:00 2001 From: Joseph Date: Thu, 5 Aug 2021 16:07:57 +0100 Subject: [PATCH 2/6] [SoundcloudBridge] Move comment --- bridges/SoundcloudBridge.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bridges/SoundcloudBridge.php b/bridges/SoundcloudBridge.php index 835119e1edc..6a3d2875711 100644 --- a/bridges/SoundcloudBridge.php +++ b/bridges/SoundcloudBridge.php @@ -16,7 +16,7 @@ class SoundCloudBridge extends BridgeAbstract { 'type' => 'list', 'defaultValue' => 'tracks', 'values' => array( - 'All' => 'all', + 'All (except likes)' => 'all', 'Tracks' => 'tracks', 'Albums' => 'albums', 'Playlists' => 'playlists', @@ -27,6 +27,7 @@ class SoundCloudBridge extends BridgeAbstract { )); private $apiUrl = 'https://api-v2.soundcloud.com/'; + // Without url=http, player URL returns a 404 private $playerUrl = 'https://w.soundcloud.com/player/?url=http'; private $widgetUrl = 'https://widget.sndcdn.com/'; @@ -120,7 +121,6 @@ private function getClientID(){ private function refreshClientID(){ $this->initClientIDCache(); - // Without url=http, this returns a 404 $playerHTML = getContents($this->playerUrl) or returnServerError('Unable to get player page.'); From a39c2d0bed2701aed65c5b99774ab70d886ca6c2 Mon Sep 17 00:00:00 2001 From: Joseph Date: Sat, 18 Sep 2021 22:54:18 +0100 Subject: [PATCH 3/6] [SoundcloudBridge] Use correct endpoint for playlists --- bridges/SoundcloudBridge.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bridges/SoundcloudBridge.php b/bridges/SoundcloudBridge.php index 6a3d2875711..443f72769f5 100644 --- a/bridges/SoundcloudBridge.php +++ b/bridges/SoundcloudBridge.php @@ -167,6 +167,10 @@ private function getUserItems($userId, $type) { $parameters = array('limit' => 10); $endpoint = 'users/' . $userId . '/' . $type; + if ($type === 'playlists') { + $endpoint = 'users/' . $userId . '/playlists_without_albums'; + } + if ($type === 'all') { $endpoint = 'stream/users/' . $userId; } From 55fdc1f657cc98b06e8e3db868b5877c7b15dc1e Mon Sep 17 00:00:00 2001 From: Joseph Date: Sat, 18 Sep 2021 22:55:32 +0100 Subject: [PATCH 4/6] [SoundcloudBridge] Add method getTrackList() --- bridges/SoundcloudBridge.php | 60 ++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/bridges/SoundcloudBridge.php b/bridges/SoundcloudBridge.php index 443f72769f5..c51f5fce481 100644 --- a/bridges/SoundcloudBridge.php +++ b/bridges/SoundcloudBridge.php @@ -45,24 +45,39 @@ public function collectData() { $this->feedTitle = $res->username; $this->feedIcon = $res->avatar_url; - $tracks = $this->getUserItems($res->id, $this->getInput('t')) + $apiItems = $this->getUserItems($res->id, $this->getInput('t')) or returnServerError('No results for ' . $this->getInput('t')); $hasTrackObject = array('all', 'reposts', 'likes'); - foreach ($tracks->collection as $index => $track) { + foreach ($apiItems->collection as $index => $apiItem) { if (in_array($this->getInput('t'), $hasTrackObject) === true) { - $track = $track->track; + $apiItem = $apiItem->track; } $item = array(); - $item['author'] = $track->user->username; - $item['title'] = $track->user->username . ' - ' . $track->title; - $item['timestamp'] = strtotime($track->created_at); - $item['content'] = nl2br($track->description); - $item['enclosures'][] = $track->artwork_url; - $item['id'] = $track->permalink_url; - $item['uri'] = $track->permalink_url; + $item['author'] = $apiItem->user->username; + $item['title'] = $apiItem->user->username . ' - ' . $apiItem->title; + $item['timestamp'] = strtotime($apiItem->created_at); + + $description = nl2br($apiItem->description); + + $item['content'] = <<{$description}

+ HTML; + + if (isset($apiItem->tracks) && $apiItem->track_count > 0) { + $list = $this->getTrackList($apiItem->tracks); + + $item['content'] .= <<Tracks ({$apiItem->track_count})

+ {$list} + HTML; + } + + $item['enclosures'][] = $apiItem->artwork_url; + $item['id'] = $apiItem->permalink_url; + $item['uri'] = $apiItem->permalink_url; $this->items[] = $item; if (count($this->items) >= 10) { @@ -196,4 +211,29 @@ private function getApi($endpoint, $parameters) { return json_decode(getContents($url)); } } + + private function getTrackList($tracks) { + $trackids = ''; + + foreach ($tracks as $track) { + $trackids .= $track->id . ','; + } + + $apiItems = $this->getApi( + 'tracks', array('ids' => $trackids) + ); + + $list = ''; + foreach($apiItems as $track) { + $list .= <<{$track->user->username} — {$track->title} + HTML; + } + + $html = <<{$list} + HTML; + + return $html; + } } From 8df2a53a51ee0007734fd9e287ba36c2a30814e1 Mon Sep 17 00:00:00 2001 From: Joseph Date: Sat, 18 Sep 2021 23:26:13 +0100 Subject: [PATCH 5/6] [SoundcloudBridge] Fix Lint --- bridges/SoundcloudBridge.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bridges/SoundcloudBridge.php b/bridges/SoundcloudBridge.php index c51f5fce481..d2375a05707 100644 --- a/bridges/SoundcloudBridge.php +++ b/bridges/SoundcloudBridge.php @@ -72,7 +72,7 @@ public function collectData() { $item['content'] .= <<Tracks ({$apiItem->track_count})

{$list} - HTML; +HTML; } $item['enclosures'][] = $apiItem->artwork_url; @@ -232,7 +232,7 @@ private function getTrackList($tracks) { $html = <<{$list} - HTML; +HTML; return $html; } From 49eca729a5f8dfd43643771f7357cf71f48d21c0 Mon Sep 17 00:00:00 2001 From: Joseph Date: Sat, 18 Sep 2021 23:31:40 +0100 Subject: [PATCH 6/6] [SoundcloudBridge] Fix Lint (again) --- bridges/SoundcloudBridge.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bridges/SoundcloudBridge.php b/bridges/SoundcloudBridge.php index d2375a05707..86d069a0714 100644 --- a/bridges/SoundcloudBridge.php +++ b/bridges/SoundcloudBridge.php @@ -64,7 +64,7 @@ public function collectData() { $item['content'] = <<{$description}

- HTML; +HTML; if (isset($apiItem->tracks) && $apiItem->track_count > 0) { $list = $this->getTrackList($apiItem->tracks); @@ -227,7 +227,7 @@ private function getTrackList($tracks) { foreach($apiItems as $track) { $list .= <<{$track->user->username} — {$track->title} - HTML; +HTML; } $html = <<