Skip to content

Latest commit

 

History

History
570 lines (459 loc) · 14.7 KB

File metadata and controls

570 lines (459 loc) · 14.7 KB

Setlist Player - Technical Architecture

This document provides detailed technical information about the plugin's architecture, API integrations, and implementation patterns.

File Structure

setlist-player/
├── includes/
│   ├── class-setlist-player-youtube-api.php      # YouTube Data API v3 wrapper
│   ├── class-setlist-player-settings.php         # Admin settings page
│   └── class-setlist-player-rest-controller.php  # REST API controller
├── src/                                           # Block editor assets (Phase 3+)
├── tests/                                         # PHPUnit tests
├── bin/                                           # Development scripts
└── setlist-player.php                             # Main plugin file

YouTube Data API v3 Integration

API Wrapper Class: Setlist_Player_YouTube_API

Location: includes/class-setlist-player-youtube-api.php

Responsibility: Handle all YouTube Data API v3 interactions using WordPress HTTP API.

Key Methods:

class Setlist_Player_YouTube_API {
    /**
     * Validate API key by making a test call.
     *
     * @param string $api_key The YouTube API key to validate.
     * @return bool|WP_Error True if valid, WP_Error on failure.
     */
    public function validate_api_key( $api_key ) {
        // Test endpoint: videos.list with a known video ID
        // Quota cost: 1 unit
    }

    /**
     * Search for videos by keyword.
     *
     * @param string $query Search query.
     * @param int    $max_results Number of results (default 5).
     * @return array|WP_Error Array of video results or WP_Error.
     */
    public function search_videos( $query, $max_results = 5 ) {
        // Endpoint: /youtube/v3/search
        // Quota cost: 100 units per search
    }

    /**
     * Get video details by ID.
     *
     * @param string $video_id YouTube video ID.
     * @return array|WP_Error Video details or WP_Error.
     */
    public function get_video_details( $video_id ) {
        // Endpoint: /youtube/v3/videos
        // Quota cost: 1 unit per video
    }
}

YouTube API Endpoints

1. API Key Validation

Endpoint: GET https://www.googleapis.com/youtube/v3/videos

Parameters:

  • part=id (required)
  • id=dQw4w9WgXcQ (test video ID)
  • key=YOUR_API_KEY (required)

Quota Cost: 1 unit

Example Request:

$url = add_query_arg(
    array(
        'part' => 'id',
        'id'   => 'dQw4w9WgXcQ',
        'key'  => $api_key,
    ),
    'https://www.googleapis.com/youtube/v3/videos'
);

$response = wp_remote_get( $url, array( 'timeout' => 15 ) );

Success Response:

{
  "items": [
    {
      "kind": "youtube#video",
      "id": "dQw4w9WgXcQ"
    }
  ]
}

Error Response:

{
  "error": {
    "code": 400,
    "message": "API key not valid. Please pass a valid API key.",
    "errors": [...]
  }
}

2. Video Search

Endpoint: GET https://www.googleapis.com/youtube/v3/search

Parameters:

  • part=snippet (required)
  • q=search query (required)
  • type=video (filter to videos only)
  • maxResults=5 (1-50)
  • key=YOUR_API_KEY (required)

Quota Cost: 100 units

Example Request:

$url = add_query_arg(
    array(
        'part'       => 'snippet',
        'q'          => 'kyuss gardenia',
        'type'       => 'video',
        'maxResults' => 5,
        'key'        => $api_key,
    ),
    'https://www.googleapis.com/youtube/v3/search'
);

$response = wp_remote_get( $url, array( 'timeout' => 15 ) );

Success Response:

{
  "items": [
    {
      "id": {
        "videoId": "VIDEO_ID"
      },
      "snippet": {
        "title": "Kyuss - Gardenia",
        "description": "...",
        "thumbnails": {
          "default": { "url": "...", "width": 120, "height": 90 },
          "medium": { "url": "...", "width": 320, "height": 180 },
          "high": { "url": "...", "width": 480, "height": 360 }
        },
        "channelTitle": "Channel Name"
      }
    }
  ]
}

3. Video Details

Endpoint: GET https://www.googleapis.com/youtube/v3/videos

Parameters:

  • part=snippet,contentDetails (required)
  • id=VIDEO_ID (required, comma-separated for multiple)
  • key=YOUR_API_KEY (required)

Quota Cost: 1 unit per video

Example Request:

$url = add_query_arg(
    array(
        'part' => 'snippet,contentDetails',
        'id'   => 'c_gCpMwqM34',
        'key'  => $api_key,
    ),
    'https://www.googleapis.com/youtube/v3/videos'
);

$response = wp_remote_get( $url, array( 'timeout' => 15 ) );

Success Response:

{
  "items": [
    {
      "id": "c_gCpMwqM34",
      "snippet": {
        "title": "Kyuss - Green Machine",
        "description": "...",
        "thumbnails": { ... },
        "channelTitle": "..."
      },
      "contentDetails": {
        "duration": "PT3M33S"
      }
    }
  ]
}

Error Handling Pattern

$response = wp_remote_get( $url, array( 'timeout' => 15 ) );

if ( is_wp_error( $response ) ) {
    return new WP_Error(
        'youtube_api_error',
        __( 'Failed to connect to YouTube API.', 'setlist-player' ),
        array( 'status' => 500 )
    );
}

$code = wp_remote_retrieve_response_code( $response );
if ( 200 !== $code ) {
    $body = json_decode( wp_remote_retrieve_body( $response ), true );
    $message = $body['error']['message'] ?? __( 'Unknown YouTube API error.', 'setlist-player' );

    return new WP_Error( 'youtube_api_error', $message, array( 'status' => $code ) );
}

return json_decode( wp_remote_retrieve_body( $response ), true );

WordPress REST API

REST Controller: Setlist_Player_REST_Controller

Location: includes/class-setlist-player-rest-controller.php

Extends: WP_REST_Controller

Namespace: setlist-player/v1

Endpoints:

1. Search Videos

Route: GET /wp-json/setlist-player/v1/youtube/search

Parameters:

  • q (string, required): Search query

Permission: edit_posts capability

Example Request:

fetch('/wp-json/setlist-player/v1/youtube/search?q=kyuss')
    .then(response => response.json())
    .then(data => console.log(data));

Success Response (200):

{
  "videos": [
    {
      "id": "VIDEO_ID",
      "title": "Kyuss - Gardenia",
      "thumbnail": "https://...",
      "channelTitle": "Channel Name"
    }
  ]
}

Error Response (4xx/5xx):

{
  "code": "youtube_api_error",
  "message": "API key not configured",
  "data": {
    "status": 500
  }
}

2. Get Video Details

Route: GET /wp-json/setlist-player/v1/youtube/video/{id}

Parameters:

  • id (string, in path): YouTube video ID

Permission: edit_posts capability

Example Request:

fetch('/wp-json/setlist-player/v1/youtube/video/c_gCpMwqM34')
    .then(response => response.json())
    .then(data => console.log(data));

Success Response (200):

{
  "id": "c_gCpMwqM34",
  "title": "Kyuss - Green Machine",
  "description": "...",
  "thumbnail": "https://...",
  "duration": "3:33",
  "channelTitle": "Channel Name"
}

REST Controller Implementation Pattern

class Setlist_Player_REST_Controller extends WP_REST_Controller {

    protected $namespace = 'setlist-player/v1';
    protected $rest_base = 'youtube';
    private $youtube_api;

    public function __construct() {
        $this->youtube_api = new Setlist_Player_YouTube_API();
    }

    public function register_routes() {
        // Search videos endpoint
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/search',
            array(
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => array( $this, 'get_items' ),
                'permission_callback' => array( $this, 'get_items_permissions_check' ),
                'args'                => $this->get_collection_params(),
            )
        );

        // Get single video endpoint
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/video/(?P<id>[\w-]+)',
            array(
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => array( $this, 'get_item' ),
                'permission_callback' => array( $this, 'get_item_permissions_check' ),
            )
        );
    }

    public function get_items_permissions_check( $request ) {
        return current_user_can( 'edit_posts' );
    }

    public function get_item_permissions_check( $request ) {
        return current_user_can( 'edit_posts' );
    }

    public function get_items( $request ) {
        $query = $request->get_param( 'q' );
        $results = $this->youtube_api->search_videos( $query );

        if ( is_wp_error( $results ) ) {
            return $results;
        }

        return rest_ensure_response( $results );
    }

    public function get_item( $request ) {
        $video_id = $request->get_param( 'id' );
        $result = $this->youtube_api->get_video_details( $video_id );

        if ( is_wp_error( $result ) ) {
            return $result;
        }

        return rest_ensure_response( $result );
    }

    public function get_collection_params() {
        return array(
            'q' => array(
                'required'          => true,
                'type'              => 'string',
                'sanitize_callback' => 'sanitize_text_field',
            ),
        );
    }
}

Settings Page

Settings Class: Setlist_Player_Settings

Location: includes/class-setlist-player-settings.php

Responsibility: Admin settings page for YouTube API key configuration.

Key Features:

  • Settings page under "Settings" → "Setlist Player"
  • Single text field for API key
  • Server-side validation on save using YouTube API wrapper
  • Admin notices for success/error states
  • API key stored in WordPress options (encrypted recommended for production)

Settings API Pattern:

class Setlist_Player_Settings {

    private $option_name = 'setlist_player_youtube_api_key';

    public function register_settings() {
        register_setting(
            'setlist_player_settings',
            $this->option_name,
            array(
                'type'              => 'string',
                'sanitize_callback' => array( $this, 'validate_api_key' ),
            )
        );

        add_settings_section(
            'setlist_player_api_settings',
            __( 'YouTube API Configuration', 'setlist-player' ),
            array( $this, 'render_section_description' ),
            'setlist-player-settings'
        );

        add_settings_field(
            'youtube_api_key',
            __( 'YouTube API Key', 'setlist-player' ),
            array( $this, 'render_api_key_field' ),
            'setlist-player-settings',
            'setlist_player_api_settings'
        );
    }

    public function validate_api_key( $api_key ) {
        $api_key = sanitize_text_field( $api_key );
        $old_value = get_option( $this->option_name );

        if ( empty( $api_key ) ) {
            add_settings_error(
                $this->option_name,
                'api_key_empty',
                __( 'API key cannot be empty.', 'setlist-player' )
            );
            return $old_value;
        }

        $youtube_api = new Setlist_Player_YouTube_API();
        $is_valid = $youtube_api->validate_api_key( $api_key );

        if ( is_wp_error( $is_valid ) ) {
            add_settings_error(
                $this->option_name,
                'api_key_invalid',
                sprintf(
                    /* translators: %s: error message from YouTube API */
                    __( 'API key validation failed: %s', 'setlist-player' ),
                    $is_valid->get_error_message()
                )
            );
            return $old_value;
        }

        add_settings_error(
            $this->option_name,
            'api_key_saved',
            __( 'API key validated and saved successfully.', 'setlist-player' ),
            'success'
        );

        return $api_key;
    }
}

Security Considerations

  1. API Key Storage: Stored in WordPress options table. Consider encrypting for production.
  2. REST API Permissions: Only users with edit_posts capability can access endpoints.
  3. Nonce Validation: Handled automatically by WordPress REST API.
  4. Input Sanitization: All user inputs sanitized via sanitize_text_field() and validated.
  5. Output Escaping: All output properly escaped using esc_html(), esc_url(), etc.
  6. Rate Limiting: YouTube API has daily quota limits (10,000 units/day default).

Testing Strategy

Unit Tests (PHPUnit)

Test Coverage:

  • YouTube API wrapper methods (mock HTTP responses)
  • Settings validation callbacks
  • REST controller permission checks
  • REST controller response formatting

Example Test:

class Test_YouTube_API extends WP_UnitTestCase {

    public function test_validate_api_key_with_valid_key() {
        // Mock wp_remote_get to return success
        add_filter( 'pre_http_request', function() {
            return array(
                'response' => array( 'code' => 200 ),
                'body'     => json_encode( array( 'items' => array() ) ),
            );
        });

        $youtube_api = new Setlist_Player_YouTube_API();
        $result = $youtube_api->validate_api_key( 'test-key' );

        $this->assertTrue( $result );
    }
}

Integration Tests

Test Coverage:

  • Settings page save functionality
  • REST API endpoint responses
  • Error handling flows

Plugin Initialization

Main Plugin File (setlist-player.php):

// Load dependencies.
require_once SETLIST_PLAYER_PLUGIN_DIR . 'includes/class-setlist-player-youtube-api.php';
require_once SETLIST_PLAYER_PLUGIN_DIR . 'includes/class-setlist-player-settings.php';
require_once SETLIST_PLAYER_PLUGIN_DIR . 'includes/class-setlist-player-rest-controller.php';

// Initialize settings page.
$setlist_player_settings = new Setlist_Player_Settings();
add_action( 'admin_menu', array( $setlist_player_settings, 'add_settings_page' ) );
add_action( 'admin_init', array( $setlist_player_settings, 'register_settings' ) );

// Initialize REST API.
add_action( 'rest_api_init', function() {
    $controller = new Setlist_Player_REST_Controller();
    $controller->register_routes();
});

Future Considerations

Caching Strategy

  • Cache YouTube API responses for 24 hours to reduce quota usage
  • Use WordPress Transients API for temporary storage
  • Cache key pattern: setlist_player_video_{video_id}

Error Logging

  • Log YouTube API errors to debug.log in WP_DEBUG mode
  • Track quota usage for admin monitoring

API Key Encryption

  • Use WordPress salts/keys for encryption
  • Consider using openssl_encrypt() for API key storage