From b48bc77c22e13d93aa9f8216b0503a77305fd078 Mon Sep 17 00:00:00 2001 From: Roliga Date: Fri, 30 Oct 2020 14:50:36 +0100 Subject: [PATCH] [TwitchBridge] Switch to unofficial GraphQL API (#1829) * [TwitchBridge] Switch to unofficial GraphQL API The GraphQL API that the twitch.tv website uses has a lot more information available than the official APIs. Hopefully it'll be stable. --- bridges/TwitchBridge.php | 188 ++++++++++++++++++++++++++------------- 1 file changed, 125 insertions(+), 63 deletions(-) diff --git a/bridges/TwitchBridge.php b/bridges/TwitchBridge.php index 39b46010..8b43a317 100644 --- a/bridges/TwitchBridge.php +++ b/bridges/TwitchBridge.php @@ -20,7 +20,9 @@ class TwitchBridge extends BridgeAbstract { 'All' => 'all', 'Archive' => 'archive', 'Highlights' => 'highlight', - 'Uploads' => 'upload' + 'Uploads' => 'upload', + 'Past Premieres' => 'past_premiere', + 'Premiere Uploads' => 'premiere_upload' ), 'defaultValue' => 'archive' ) @@ -32,43 +34,90 @@ class TwitchBridge extends BridgeAbstract { */ const CLIENT_ID = 'kimne78kx3ncx6brgo4mv6wki5h1ko'; + const API_ENDPOINT = 'https://gql.twitch.tv/gql'; + const BROADCAST_TYPES = array( + 'all' => array( + 'ARCHIVE', + 'HIGHLIGHT', + 'UPLOAD', + 'PAST_PREMIERE', + 'PREMIERE_UPLOAD' + ), + 'archive' => 'ARCHIVE', + 'highlight' => 'HIGHLIGHT', + 'upload' => 'UPLOAD', + 'past_premiere' => 'PAST_PREMIERE', + 'premiere_upload' => 'PREMIERE_UPLOAD' + ); + public function collectData(){ - // get channel user - $query_data = array( - 'login' => $this->getInput('channel') + $query = <<<'EOD' +query VODList($channel: String!, $types: [BroadcastType!]) { + user(login: $channel) { + displayName + videos(types: $types, sort: TIME) { + edges { + node { + id + title + publishedAt + lengthSeconds + viewCount + thumbnailURLs(width: 640, height: 360) + previewThumbnailURL(width: 640, height: 360) + description + tags + contentTags { + isLanguageTag + localizedName + } + game { + displayName + } + moments(momentRequestType: VIDEO_CHAPTER_MARKERS) { + edges { + node { + description + positionMilliseconds + } + } + } + } + } + } + } +} +EOD; + $variables = array( + 'channel' => $this->getInput('channel'), + 'types' => self::BROADCAST_TYPES[$this->getInput('type')] ); - $users = $this->apiGet('users', $query_data)->users; - if(count($users) === 0) - returnClientError('User "' - . $this->getInput('channel') - . '" could not be found'); - $user = $users[0]; + $data = $this->apiRequest($query, $variables); - // get video list - $query_endpoint = 'channels/' . $user->_id . '/videos'; - $query_data = array( - 'broadcast_type' => $this->getInput('type'), - 'limit' => 10 - ); - $videos = $this->apiGet($query_endpoint, $query_data)->videos; + $user = $data->user; + foreach($user->videos->edges as $edge) { + $video = $edge->node; + + $url = 'https://www.twitch.tv/videos/' . $video->id; - foreach($videos as $video) { $item = array( - 'uri' => $video->url, + 'uri' => $url, 'title' => $video->title, - 'timestamp' => $video->published_at, - 'author' => $video->channel->display_name, + 'timestamp' => $video->publishedAt, + 'author' => $user->displayName, ); // Add categories for tags and played game - $item['categories'] = array_filter(explode(' ', $video->tag_list)); - if(!empty($video->game)) - $item['categories'][] = $video->game; + $item['categories'] = $video->tags; + if(!is_null($video->game)) + $item['categories'][] = $video->game->displayName; + foreach($video->contentTags as $tag) + if(!$tag->isLanguageTag) + $item['categories'][] = $tag->localizedName; // Add enclosures for thumbnails from a few points in the video - $item['enclosures'] = array(); - foreach($video->thumbnails->large as $thumbnail) - $item['enclosures'][] = $thumbnail->url; + // Thumbnail list has duplicate entries sometimes so remove those + $item['enclosures'] = array_unique($video->thumbnailURLs); /* * Content format example: @@ -86,44 +135,45 @@ class TwitchBridge extends BridgeAbstract { * */ $item['content'] = '

' - . $video->description_html + . $video->description // in markdown format . '

Duration: ' - . $this->formatTimestampTime($video->length) + . $this->formatTimestampTime($video->lengthSeconds) . '
Views: ' - . $video->views + . $video->viewCount . '

'; // Add played games list to content - $video_id = trim($video->_id, 'v'); // _id gives 'v1234' but API wants '1234' - $markers = $this->apiGet('videos/' . $video_id . '/markers')->markers; - $item['content'] .= '

Played games: