array( 'name' => 'Channel', 'type' => 'text', 'required' => true, 'title' => 'Lowercase channel name as seen in channel URL' ), 'type' => array( 'name' => 'Type', 'type' => 'list', 'values' => array( 'All' => 'all', 'Archive' => 'archive', 'Highlights' => 'highlight', 'Uploads' => 'upload', 'Past Premieres' => 'past_premiere', 'Premiere Uploads' => 'premiere_upload' ), 'defaultValue' => 'archive' ) )); /* * Official instructions for obtaining your own client ID can be found here: * https://dev.twitch.tv/docs/v5/#getting-a-client-id */ 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(){ $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')] ); $data = $this->apiRequest($query, $variables); $user = $data->user; foreach($user->videos->edges as $edge) { $video = $edge->node; $url = 'https://www.twitch.tv/videos/' . $video->id; $item = array( 'uri' => $url, 'title' => $video->title, 'timestamp' => $video->publishedAt, 'author' => $user->displayName, ); // Add categories for tags and played 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 // Thumbnail list has duplicate entries sometimes so remove those $item['enclosures'] = array_unique($video->thumbnailURLs); /* * Content format example: * * [Preview Image] * * Some optional video description. * * Duration: 1:23:45 * Views: 123 * * Played games: * * 00:00:00 Game 1 * * 00:12:34 Game 2 * */ $item['content'] = '

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

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

'; // Add played games list to content $item['content'] .= '

Played games:

'; $item['categories'] = array_unique($item['categories']); $this->items[] = $item; } } // e.g. 01:53:27 private function formatTimestampTime($seconds) { return sprintf('%02d:%02d:%02d', floor($seconds / 3600), ($seconds / 60) % 60, $seconds % 60); } // e.g. 01h53m27s private function formatQueryTime($seconds) { return sprintf('%02dh%02dm%02ds', floor($seconds / 3600), ($seconds / 60) % 60, $seconds % 60); } // GraphQL: https://graphql.org/ // Tool for developing/testing queries: https://github.com/skevy/graphiql-app private function apiRequest($query, $variables) { $request = array( 'query' => $query, 'variables' => $variables ); $header = array( 'Client-ID: ' . self::CLIENT_ID ); $opts = array( CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => json_encode($request) ); Debug::log("Sending GraphQL query:\n" . $query); Debug::log("Sending GraphQL variables:\n" . json_encode($variables, JSON_PRETTY_PRINT)); $response = json_decode(getContents(self::API_ENDPOINT, $header, $opts)) or returnServerError('API request to "' . self::API_ENDPOINT . '" failed.'); Debug::log("Got GraphQL response:\n" . json_encode($response, JSON_PRETTY_PRINT)); if(isset($response->errors)) { $messages = array_column($response->errors, 'message'); returnServerError('API error(s): ' . implode("\n", $messages)); } return $response->data; } public function getName(){ if(!is_null($this->getInput('channel'))) { return $this->getInput('channel') . ' twitch videos'; } return parent::getName(); } public function getURI(){ if(!is_null($this->getInput('channel'))) { return self::URI . $this->getInput('channel'); } return parent::getURI(); } public function detectParameters($url){ $params = array(); // Matches e.g. https://www.twitch.tv/someuser/videos?filter=archives $regex = '/^(https?:\/\/)? (www\.)? twitch\.tv\/ ([^\/&?\n]+) \/videos\?.*filter= (all|archive|highlight|upload)/x'; if(preg_match($regex, $url, $matches) > 0) { $params['channel'] = urldecode($matches[3]); $params['type'] = $matches[4]; return $params; } return null; } }