/** * Get RacingTodayZA YouTube videos for a specific date. * * Example: * * /wp-json/racing/v1/youtube-videos?date=2026-08-25 * * Returns JSON: * * { * "success": true, * "date": "2026-08-25", * "count": 5, * "videos": [ * { * "video_id": "...", * "title": "...", * "url": "...", * "date": "..." * } * ] * } */ function get_racing_today_youtube_videos( $requested_date = null ) { /* * --------------------------------------------------------- * SETTINGS * --------------------------------------------------------- */ $api_key = 'AIzaSyC7nYG2Prf6vx70n2fAjT7JS4tJxgkmjac'; $handle = '@RacingTodayZA'; /* * --------------------------------------------------------- * WORDPRESS TIMEZONE * --------------------------------------------------------- */ $timezone = wp_timezone(); /* * --------------------------------------------------------- * DATE * * If no date is supplied, use today. * * Expected format: * YYYY-MM-DD * --------------------------------------------------------- */ if ( empty( $requested_date ) ) { $requested_date = current_time( 'Y-m-d' ); } /* * Validate date. */ $date_object = DateTime::createFromFormat( 'Y-m-d', $requested_date, $timezone ); if ( !$date_object || $date_object->format( 'Y-m-d' ) !== $requested_date ) { return array( 'success' => false, 'error' => 'Invalid date. Use YYYY-MM-DD.', ); } /* * --------------------------------------------------------- * CREATE DATE RANGE * * Example: * * date = 2026-08-25 * * We want: * * 2026-08-25 00:00:00 * through * 2026-08-26 00:00:00 * * We convert these to UTC because YouTube API expects * RFC 3339 timestamps. * --------------------------------------------------------- */ $start = clone $date_object; $end = clone $date_object; $end->modify( '+1 day' ); /* * Convert to UTC. */ $utc_timezone = new DateTimeZone( 'UTC' ); $start->setTimezone( $utc_timezone ); $end->setTimezone( $utc_timezone ); /* * RFC 3339 format. */ $published_after = $start->format( 'Y-m-d\TH:i:s\Z' ); $published_before = $end->format( 'Y-m-d\TH:i:s\Z' ); /* * --------------------------------------------------------- * STEP 1 * * Find the channel ID from @RacingTodayZA * --------------------------------------------------------- */ $channel_url = add_query_arg( array( 'part' => 'id,snippet', 'forHandle' => $handle, 'key' => $api_key, ), 'https://www.googleapis.com/youtube/v3/channels' ); $response = wp_remote_get( $channel_url, array( 'timeout' => 30, ) ); if ( is_wp_error( $response ) ) { return array( 'success' => false, 'error' => $response->get_error_message(), ); } /* * HTTP status. */ $http_code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); $data = json_decode( $body, true ); /* * API error. */ if ( $http_code < 200 || $http_code >= 300 ) { return array( 'success' => false, 'error' => 'YouTube channel API request failed.', 'http_code' => $http_code, 'api_response' => $data, ); } /* * Channel not found. */ if ( empty( $data['items'] ) || empty( $data['items'][0]['id'] ) ) { return array( 'success' => false, 'error' => 'YouTube channel not found.', 'api_response' => $data, ); } $channel_id = $data['items'][0]['id']; $channel_name = $data['items'][0]['snippet']['title'] ?? ''; /* * --------------------------------------------------------- * STEP 2 * * Search for videos published on requested date. * --------------------------------------------------------- */ $videos = array(); $page_token = ''; do { $params = array( /* * Search resource only needs snippet. */ 'part' => 'snippet', /* * Only this channel. */ 'channelId' => $channel_id, /* * Only videos. */ 'type' => 'video', /* * Requested date range. */ 'publishedAfter' => $published_after, 'publishedBefore' => $published_before, /* * Newest first. */ 'order' => 'date', /* * Maximum allowed. */ 'maxResults' => 50, /* * API key. */ 'key' => $api_key, ); /* * Pagination. */ if ( !empty( $page_token ) ) { $params['pageToken'] = $page_token; } /* * Build API URL. */ $search_url = add_query_arg( $params, 'https://www.googleapis.com/youtube/v3/search' ); /* * Request. */ $response = wp_remote_get( $search_url, array( 'timeout' => 30, ) ); if ( is_wp_error( $response ) ) { return array( 'success' => false, 'error' => $response->get_error_message(), ); } /* * HTTP status. */ $http_code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); $data = json_decode( $body, true ); /* * API error. */ if ( $http_code < 200 || $http_code >= 300 ) { return array( 'success' => false, 'error' => 'YouTube search API request failed.', 'http_code' => $http_code, 'api_response' => $data, ); } /* * YouTube API error. */ if ( !empty( $data['error'] ) ) { return array( 'success' => false, 'error' => 'YouTube API error.', 'api_response' => $data['error'], ); } /* * ----------------------------------------------------- * PROCESS RESULTS * ----------------------------------------------------- */ if ( !empty( $data['items'] ) ) { foreach ( $data['items'] as $item ) { /* * Make sure this is a video. */ if ( empty( $item['id']['videoId'] ) ) { continue; } $video_id = $item['id']['videoId']; $title = $item['snippet']['title'] ?? ''; $published_at = $item['snippet']['publishedAt'] ?? ''; /* * ------------------------------------------------- * Add video. * ------------------------------------------------- */ $videos[] = array( 'video_id' => $video_id, 'title' => $title, 'url' => 'https://www.youtube.com/watch?v=' . $video_id, 'date' => $published_at, 'date_local' => !empty( $published_at ) ? ( new DateTime( $published_at, new DateTimeZone( 'UTC' ) ) )->setTimezone( $timezone )->format( 'Y-m-d H:i:s' ) : '', ); } } /* * ----------------------------------------------------- * NEXT PAGE * ----------------------------------------------------- */ $page_token = $data['nextPageToken'] ?? ''; } while ( !empty( $page_token ) ); /* * --------------------------------------------------------- * RETURN * --------------------------------------------------------- */ return array( 'success' => true, 'channel' => array( 'id' => $channel_id, 'name' => $channel_name, 'handle' => $handle, ), 'date' => $requested_date, 'timezone' => $timezone->getName(), 'published_after' => $published_after, 'published_before' => $published_before, 'count' => count( $videos ), 'videos' => $videos, ); } /* * ============================================================= * WORDPRESS REST API ENDPOINT * ============================================================= */ add_action( 'rest_api_init', function () { register_rest_route( 'api/v1', '/get_yt_links', array( 'methods' => 'GET', 'callback' => function ( WP_REST_Request $request ) { /* * Get ?date=YYYY-MM-DD */ $date = $request->get_param('date'); /* * If no date is supplied, * use today's WordPress date. */ if ( empty($date) ) { $date = current_time('Y-m-d'); } /* * Get YouTube videos. */ return get_racing_today_youtube_videos($date); }, /* * ------------------------------------------------- * API KEY AUTHENTICATION * ------------------------------------------------- */ 'permission_callback' => function ( WP_REST_Request $request ) { $provided_key = $request->get_header('x-api-key'); /* * No API key supplied. */ if ( empty($provided_key) ) { return new WP_Error( 'missing_api_key', 'API key is required.', array( 'status' => 401 ) ); } /* * Invalid API key. * * hash_equals() prevents timing attacks. */ if ( !defined('REPLAYS_API_KEY') || !hash_equals( REPLAYS_API_KEY, $provided_key ) ) { return new WP_Error( 'invalid_api_key', 'Invalid API key.', array( 'status' => 403 ) ); } /* * API key is valid. */ return true; }, ) ); } ); Race Card - iRace
Tuesday, August 25, 2026

Royal Randwick Championships Banner
FWD Champions Day Banner