# Goondex Frontend API Guide **For Frontend Developers** This guide explains how to interact with the Goondex API using JavaScript. No backend knowledge required - just JavaScript, HTML, CSS, and Bootstrap skills! ## Table of Contents 1. [Getting Started](#getting-started) 2. [Base URL](#base-url) 3. [Data Models](#data-models) 4. [API Endpoints](#api-endpoints) 5. [Common Workflows](#common-workflows) 6. [Error Handling](#error-handling) 7. [Real-time Progress Updates](#real-time-progress-updates) 8. [Complete Examples](#complete-examples) --- ## Getting Started All API endpoints return JSON data. You can use the `fetch` API to make requests from your JavaScript code. ### Basic API Response Format All API responses follow this structure: ```javascript { "success": true, // boolean - whether the operation succeeded "message": "Success text", // string - human-readable message "data": { ... } // object - actual data (optional) } ``` --- ## Base URL The API server runs at: `http://localhost:8080` (by default) All endpoints are prefixed with this URL. --- ## Data Models ### Performer ```javascript { "id": 123, "name": "Performer Name", "aliases": "Alias1, Alias2", // Physical Attributes "gender": "female", // male/female/trans/other "birthday": "1995-03-15", // YYYY-MM-DD "astrology": "Pisces", "birthplace": "Los Angeles, CA", "ethnicity": "Caucasian", "nationality": "US", // ISO country code (US, GB, FR, etc.) "country": "United States", "eye_color": "Blue", "hair_color": "Blonde", "height": 165, // centimeters "weight": 55, // kilograms "measurements": "34C-24-36", "cup_size": "34C", "tattoo_description": "Dragon on left shoulder", "piercing_description": "Nose piercing", "boob_job": "False", // "True" or "False" as string // Career "career": "2015-2023", "career_start_year": 2015, "career_end_year": 2023, "date_of_death": "", // YYYY-MM-DD if applicable "active": true, // Media "image_path": "/path/to/image.jpg", "image_url": "https://example.com/image.jpg", "poster_url": "https://example.com/poster.jpg", "bio": "Biography text...", // Metadata "source": "tpdb", "source_id": "abc-123-def", "source_numeric_id": 456, "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-02T12:00:00Z" } ``` ### Studio ```javascript { "id": 456, "name": "Studio Name", "parent_id": 123, // null if no parent studio "image_path": "/path/to/logo.jpg", "image_url": "https://example.com/logo.jpg", "description": "Studio description...", "source": "tpdb", "source_id": "xyz-789", "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-02T12:00:00Z" } ``` ### Scene ```javascript { "id": 789, "title": "Scene Title", "code": "SCENE-001", // DVD code or scene identifier "date": "2024-01-15", // Release date YYYY-MM-DD "studio_id": 456, "description": "Scene description...", "image_path": "/path/to/thumbnail.jpg", "image_url": "https://example.com/thumbnail.jpg", "director": "Director Name", "url": "https://example.com/scene", "source": "tpdb", "source_id": "scene-123", "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-02T12:00:00Z", // Relationships (when populated) "performers": [ /* array of Performer objects */ ], "tags": [ /* array of Tag objects */ ], "studio": { /* Studio object */ } } ``` ### Movie ```javascript { "id": 321, "title": "Movie Title", "date": "2024-01-01", // Release date "studio_id": 456, "description": "Movie description...", "director": "Director Name", "duration": 120, // Duration in minutes "image_path": "/path/to/cover.jpg", "image_url": "https://example.com/cover.jpg", "back_image_url": "https://example.com/back-cover.jpg", "url": "https://example.com/movie", "source": "tpdb", "source_id": "movie-456", "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-02T12:00:00Z", // Relationships (when populated) "scenes": [ /* array of Scene objects */ ], "performers": [ /* array of Performer objects */ ], "tags": [ /* array of Tag objects */ ], "studio": { /* Studio object */ } } ``` ### Tag ```javascript { "id": 111, "name": "Tag Name", "category_id": 5, "aliases": "Alias1, Alias2", "description": "Tag description...", "source": "tpdb", "source_id": "tag-789", "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-02T12:00:00Z" } ``` --- ## API Endpoints ### Search & Import #### 1. Import Performer by Search **Endpoint:** `POST /api/import/performer` **Description:** Search for a performer and import all matching results from TPDB. **Request Body:** ```javascript { "query": "performer name" } ``` **Example:** ```javascript const response = await fetch('http://localhost:8080/api/import/performer', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: 'Jane Doe' }) }); const result = await response.json(); // result = { // "success": true, // "message": "Imported 5 performer(s)", // "data": { "imported": 5, "found": 5 } // } ``` #### 2. Import Studio by Search **Endpoint:** `POST /api/import/studio` **Request Body:** ```javascript { "query": "studio name" } ``` **Example:** ```javascript const response = await fetch('http://localhost:8080/api/import/studio', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: 'Brazzers' }) }); const result = await response.json(); ``` #### 3. Import Scene by Search **Endpoint:** `POST /api/import/scene` **Description:** Search for a scene and import all matching results. This also imports associated performers, studio, and tags. **Request Body:** ```javascript { "query": "scene title" } ``` **Example:** ```javascript const response = await fetch('http://localhost:8080/api/import/scene', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: 'Scene Title' }) }); const result = await response.json(); ``` ### Bulk Import #### 4. Bulk Import All **Endpoint:** `POST /api/import/all` **Description:** Import all performers, studios, and scenes from your local database. This fetches full metadata from TPDB. **No Request Body Required** **Example:** ```javascript const response = await fetch('http://localhost:8080/api/import/all', { method: 'POST' }); const result = await response.json(); // result.data contains import statistics ``` #### 5. Bulk Import All Performers **Endpoint:** `POST /api/import/all-performers` **Example:** ```javascript const response = await fetch('http://localhost:8080/api/import/all-performers', { method: 'POST' }); const result = await response.json(); // result = { // "success": true, // "message": "Imported 150/200 performers", // "data": { // "total": 200, // "imported": 150, // "skipped": 50, // "errors": 0 // } // } ``` #### 6. Bulk Import All Studios **Endpoint:** `POST /api/import/all-studios` **Example:** ```javascript const response = await fetch('http://localhost:8080/api/import/all-studios', { method: 'POST' }); const result = await response.json(); ``` #### 7. Bulk Import All Scenes **Endpoint:** `POST /api/import/all-scenes` **Example:** ```javascript const response = await fetch('http://localhost:8080/api/import/all-scenes', { method: 'POST' }); const result = await response.json(); ``` ### Sync #### 8. Sync All Data **Endpoint:** `POST /api/sync` **Description:** Synchronize all data with TPDB to get the latest updates. **Request Body (Optional):** ```javascript { "force": false // Set to true to force sync even if recently synced } ``` **Example:** ```javascript const response = await fetch('http://localhost:8080/api/sync', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ force: true }) }); const result = await response.json(); ``` #### 9. Get Sync Status **Endpoint:** `GET /api/sync/status` **Description:** Get the last sync time for all entities. **Example:** ```javascript const response = await fetch('http://localhost:8080/api/sync/status'); const result = await response.json(); // result.data contains sync status for each entity type ``` ### Global Search #### 10. Search Everything **Endpoint:** `GET /api/search?q=query` **Description:** Search across performers, studios, scenes, and tags simultaneously. **Example:** ```javascript const query = 'search term'; const response = await fetch(`http://localhost:8080/api/search?q=${encodeURIComponent(query)}`); const result = await response.json(); // result.data = { // "performers": [...], // "studios": [...], // "scenes": [...], // "tags": [...], // "total": 25 // } ``` --- ## Real-time Progress Updates For bulk imports, there are special endpoints that provide real-time progress updates using Server-Sent Events (SSE). ### Bulk Import with Progress These endpoints stream progress updates as the import happens: - `GET /api/import/all-performers/progress` - `GET /api/import/all-studios/progress` - `GET /api/import/all-scenes/progress` **Example with EventSource:** ```javascript // Create an EventSource to listen for progress updates const eventSource = new EventSource('http://localhost:8080/api/import/all-performers/progress'); eventSource.onmessage = function(event) { const update = JSON.parse(event.data); if (update.error) { console.error('Import error:', update.error); eventSource.close(); return; } if (update.complete) { console.log('Import complete!', update.result); eventSource.close(); return; } // Progress update console.log(`Progress: ${update.current}/${update.total}`); console.log(`Current item: ${update.name}`); console.log(`Status: ${update.status}`); // Update UI updateProgressBar(update.current, update.total); }; eventSource.onerror = function(error) { console.error('EventSource error:', error); eventSource.close(); }; ``` **Progress Update Format:** ```javascript { "current": 15, // Current item number "total": 100, // Total items to process "name": "Jane Doe", // Name of current item being processed "status": "importing" // Status message } ``` **Completion Format:** ```javascript { "complete": true, "result": { "total": 100, "imported": 95, "skipped": 4, "errors": 1 } } ``` --- ## Common Workflows ### 1. Search and Display Performers ```javascript // Search for performers async function searchPerformers(searchQuery) { const response = await fetch( `http://localhost:8080/api/search?q=${encodeURIComponent(searchQuery)}` ); const result = await response.json(); if (result.success) { return result.data.performers; } throw new Error(result.message); } // Display in HTML async function displayPerformers() { const performers = await searchPerformers('jane'); const container = document.getElementById('performers-list'); container.innerHTML = performers.map(p => `