Frontend Themes
Overview
The ComusThumbz frontend theme is a vanilla PHP/CSS/JS application with no framework dependencies (no Bootstrap, Tailwind, React, or Vue). All public-facing pages live at the project root, above the ct/ admin directory.
Key characteristics:
- Pure vanilla CSS with CSS custom properties (variables)
- Vanilla JavaScript with a custom
ApiClientclass - API-first architecture: frontend never queries the database directly
- 25-language translation system via JSON files
- Feature toggle system controlling 35+ features
- Mandatory click tracking via
click.phpfor all outbound links - SEO-optimized with server-side meta tag generation
- Dark mode support (feature-gated)
- Cookie consent system (GDPR compliant)
Directory Structure
/ (project root)
|
|-- index.php Homepage
|-- videos.php Video browsing
|-- videoplayer.php Video playback (HLS/MP4)
|-- videoplaylist.php Playlist viewer
|-- videofilter.php AJAX video filter endpoint
|-- galleries.php Gallery browsing
|-- galleryfilter.php AJAX gallery filter endpoint
|-- models.php Model/performer directory
|-- modeldetails.php Individual model profile
|-- modelfilter.php AJAX model filter endpoint
|-- camperformers.php Live cam browsing
|-- camperformer.php Individual cam performer
|-- camfilter.php Cam filter results
|-- categories.php Category browsing
|-- users.php Community directory
|-- userprofile.php User profile view
|-- creator.php Creator profile (public)
|-- browsecreators.php Creator discovery
|-- livestreams.php Live stream directory
|-- watchstream.php Live stream viewer
|-- click.php Click tracking gateway (CRITICAL)
|-- securemedia.php Tokenized media delivery
|
|-- assets/
| |-- css/
| | +-- style.css Master stylesheet (~15,000 lines)
| |-- js/
| | |-- api-client.js REST API client (~1,400 lines)
| | |-- analytics-tracker.js
| | |-- content-tracker.js
| | |-- impression-tracker.js
| | |-- playlist-manager.js
| | |-- tipping.js
| | |-- broadcast-schedule.js
| | |-- profile-preferences.js
| | |-- profile-security.js
| | |-- profile-verification.js
| | |-- style-editor-overlay.js
| | |-- style-loader.js
| | |-- user-permissions.js
| | |-- user-preferences.js
| | +-- video-editor.js
| +-- images/
| |-- default-avatar.jpg
| |-- video-placeholder.png
| |-- silhouette.png
| +-- (cam site logos: chaturbate, stripchat, etc.)
|
|-- includes/
| |-- header.php Master header template
| |-- footer.php Master footer template
| |-- featurehelper.php Feature toggle system
| |-- InternalApiHelper.php Server-side API calls (SEO)
| |-- SeoHelper.php Meta tag generation
| |-- UrlHelper.php SEO-friendly URL generation
| |-- styleloader.php Custom theme styles from Style Manager
| |-- bannerhelper.php Ad zone display system
| |-- dm-chat-system.php Direct messaging
| +-- streamproxy.php HLS/MP4 stream proxy
|
|-- auth/
| |-- login.php User login
| |-- register.php User registration
| |-- logout.php Logout handler
| +-- setsession.php Session setter after API login
|
|-- settings/
| |-- profile.php User profile management
| |-- creatorprofile.php Creator dashboard
| |-- creatorsettings.php Creator configuration
| |-- creatorearnings.php Earnings dashboard
| |-- creatorearningshistory.php
| |-- creatorpackages.php Subscription packages
| |-- creatororders.php Creator orders
| |-- mysubscribers.php Creator subscriber list
| |-- mysubscriptions.php User subscriptions
| |-- favorites.php Bookmarked content
| |-- playlists.php Playlist management
| |-- history.php Watch history
| |-- videoupload.php Video upload form
| |-- buytokens.php Token purchase
| |-- upgrade.php VIP upgrade
| +-- golive.php Start broadcasting
|
|-- information/
| |-- terms.php Terms & Conditions
| |-- privacy.php Privacy Notice
| |-- dmca.php DMCA Takedown
| |-- 2257.php 18 U.S.C. 2257 Compliance
| |-- cookies.php Cookie Notice
| |-- accessibility.php Accessibility Statement
| |-- content-removal.php Content Removal Request
| |-- support.php Contact Support
| |-- faq.php FAQ
| |-- trust-safety.php Trust & Safety Center
| +-- parental-controls.php Parental Controls
|
|-- lang/
| |-- Language.php Translation system class
| |-- en.json English (master, 223KB, 500+ keys)
| |-- es.json, fr.json, de.json, ... (25 languages total)
| +-- (ar, bn, da, ha, hi, id, it, ja, ko, mr, nl, pl, pt, ru, sv, sw, te, tr, ur, vi, zh, yo)
|
+-- install/
|-- index.php 7-stage installation wizard
|-- ajaxschema.php AJAX schema operations
+-- stages/ Individual installation stages (1-7)
Architecture Principles
1. API-First
The frontend never accesses the database directly. All data comes from the REST API at /ct/api/v1/.
Browser JavaScript ──> ApiClient.js ──> /ct/api/v1/ ──> Database
PHP Server-side ──> InternalApiHelper.php ──> /ct/api/v1/ ──> Database
2. No Framework CSS/JS
No Bootstrap, Tailwind, React, or Vue. The entire frontend is vanilla:
- CSS in
assets/css/style.csswith CSS custom properties - JavaScript using native
fetch,class, andasync/await
3. Feature-Gated Rendering
Every page checks feature toggles before rendering. Features can be enabled/disabled from the admin panel without code changes:
if (!isfeatureenabled('featurevideos')) {
featuredisabledredirect(('nav.videos'));
}
4. Mandatory Click Tracking
Every outbound link must route through click.php. Direct external links bypass traffic tracking and incur a 100% skim penalty.
5. Translation Everywhere
No hardcoded user-facing text. All strings use the translation system:
<?= ('videos.title') ?>
Design System (CSS)
The entire theme is defined in assets/css/style.css (~15,000 lines).
Color Palette
Page-Specific Accent Colors:
CSS Variables Reference
:root {
/ Colors /
--primary-color: #264310;
--primary-dark: #53781e;
--secondary-color: #5a8020;
--accent-color: #7b994d;
--text-primary: #2c3e50;
--text-secondary: #7f8c8d;
--text-light: #95a5a6;
--bg-primary: #ffffff;
--bg-secondary: #f8f9fa;
--bg-tertiary: #ecf0f1;
/ Status Colors /
--success: #27ae60;
--error: #e74c3c;
--warning: #f39c12;
--info: #587e1f;
/ Shadows /
--shadow-sm: 0 2px 4px rgba(0,0,0,0.1);
--shadow-md: 0 4px 6px rgba(0,0,0,0.1);
--shadow-lg: 0 8px 16px rgba(0,0,0,0.15);
/ Border Radius /
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-xl: 16px;
/ Layout /
--container-width: 1000px;
--header-height: 70px;
}
Grid System
Responsive CSS Grid with predefined column classes:
.grid { display: grid; gap: 1rem; }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-5 { grid-template-columns: repeat(5, 1fr); }
Container classes:
.container { max-width: var(--container-width); margin: 0 auto; padding: 0 1rem; }
.container-wide { max-width: 1400px; }
Flexbox utilities:
.flex { display: flex; }
.flex-between { display: flex; justify-content: space-between; align-items: center; }
.gap-1 { gap: 0.5rem; }
.gap-2 { gap: 1rem; }
.gap-3 { gap: 1.5rem; }
Component Classes
Cards:
.card { background: var(--bg-primary); border-radius: var(--radius-md); box-shadow: var(--shadow-sm); }
.card-body { padding: 1rem; }
Buttons:
.btn { padding: 0.5rem 1rem; border-radius: var(--radius-sm); cursor: pointer; }
.btn-primary { background: var(--primary-color); color: white; }
.btn-secondary { background: var(--secondary-color); color: white; }
.btn-outline { border: 1px solid var(--primary-color); color: var(--primary-color); background: transparent; }
Forms:
.form-input { width: 100%; padding: 0.5rem; border: 1px solid #ddd; border-radius: var(--radius-sm); }
.form-select { / same as form-input with dropdown arrow / }
.form-label { font-weight: 600; margin-bottom: 0.25rem; }
Page Headers:
.page-header { margin-bottom: 2rem; }
.page-title { font-size: 1.5rem; font-weight: 700; color: var(--text-primary); }
.page-subtitle { color: var(--text-secondary); margin-top: 0.25rem; }
Tabs:
.tabs-container { / wrapper / }
.tabs-nav { display: flex; border-bottom: 1px solid #eee; }
.tab-btn { padding: 0.75rem 1.5rem; cursor: pointer; }
.tab-btn.active { border-bottom: 2px solid var(--primary-color); color: var(--primary-color); }
.tab-content { display: none; }
.tab-content.active { display: block; }
Loading States:
.loading { text-align: center; padding: 2rem; }
.spinner { / CSS animated spinner / }
Pagination:
.pagination { display: flex; justify-content: center; gap: 0.5rem; }
Modals:
.modal { position: fixed; inset: 0; z-index: 1000; display: none; }
.modal-content { background: white; border-radius: var(--radius-lg); max-width: 600px; margin: auto; }
Dark Mode
Dark mode is feature-gated via featuredarkmode and persisted in localStorage.
Activation:
// Check on page load
if (localStorage.getItem('comusdarkmode') === 'true') {
document.body.classList.add('dark-mode');
document.documentElement.classList.add('dark-mode');
}
// Toggle
document.body.classList.toggle('dark-mode');
localStorage.setItem('comusdarkmode',
document.body.classList.contains('dark-mode') ? 'true' : 'false');
CSS overrides: All body.dark-mode selectors redefine colors using the same CSS variable names with dark values. The header gradient shifts from #274510/#597F20 to #1a2f0a/#2d4a14.
JavaScript Layer
ApiClient (api-client.js)
The central JavaScript class for all REST API communication (~1,400 lines).
class ApiClient {
constructor(baseUrl = null) // Auto-detects API base URL
setToken(token) // Store JWT in localStorage
getToken() // Retrieve JWT from localStorage
isAuthenticated() // Check if user has token
request(endpoint, options) // Core HTTP request method
get(endpoint, params) // GET request with query params
post(endpoint, data) // POST request with JSON body
put(endpoint, data) // PUT request
delete(endpoint) // DELETE request
}
class ApiError extends Error {
constructor(message, status, data)
}
Authentication: Bearer token stored in localStorage.getItem('authtoken'), automatically added to all requests.
Usage:
const api = new ApiClient();
// GET with query params
const videos = await api.get('/videos', { page: 1, limit: 20, sort: 'views' });
// POST with body
const result = await api.post('/auth/login', { username: 'user', password: 'pass' });
// Authenticated request
api.setToken(result.data.token);
const profile = await api.get('/users/me');
Request features:
- JSON request/response formatting
- Query string serialization
- FormData support for file uploads
- Error parsing with status codes
- Network error detection
- Session cookie inclusion (
credentials: 'include')
Tracking Scripts
Feature-Specific Scripts
Layout System (includes/)
header.php
The master header template included on every frontend page.
Initialization sequence:
- Start session (if not active)
- Detect and set language from URL param, session, cookie, or browser
- Load database connection from config
- Load feature toggles (cached for 5 minutes)
- Load style overrides (Style Manager)
- Load banner helper and SEO helper
- Check maintenance mode (admin bypass allowed)
- Render HTML
<head>with SEO meta tags - Render navigation bar
Navigation elements:
- Logo / home link
- Main nav: Videos, Galleries, Models, Live Cams, Categories, Users (each feature-gated)
- Search bar
- Language selector
- User menu: Login/Register or Profile dropdown
- Creator admin link (if user is a creator)
- Token balance display (if applicable)
- Dark mode toggle (if
featuredarkmodeenabled)
Header gradient: linear-gradient(135deg, #274510 0%, #597F20 100%)
footer.php
The master footer template.
Contents:
- Banner zone (FOOTER ad placement)
- Footer navigation links (all routed through
click.php): - Terms, Privacy, DMCA, 2257, Cookie Notice, Accessibility
- Content Removal, Support, FAQ, Trust & Safety, Parental Controls
- Dark mode toggle button
- Cookie consent banner and settings modal
- Essential Cookies (always active)
- Functional Cookies (toggleable)
- Analytics Cookies (toggleable)
- Targeting/Advertising Cookies (toggleable)
featurehelper.php
Controls which features are visible on the frontend.
Constants:
FEATURECACHETTL = 300(5-minute cache)
Key functions:
isfeatureenabled('featurekey') // Check if feature is on
canaccessfeature('featurekey') // Check user access
featuredisabledredirect($message) // Redirect with message
Feature categories (35+ features):
InternalApiHelper.php
Server-side HTTP client for calling the REST API from PHP (used for SEO pre-loading).
$api = InternalApiHelper::getInstance(); // Singleton
$video = $api->getVideoForSeo($videoId); // Get video for meta tags
$data = $api->get('/endpoint', $params); // Generic GET
$result = $api->post('/endpoint', $body); // Generic POST
$error = $api->getLastError(); // Get error details
$code = $api->getLastHttpCode(); // Get HTTP status
Timeout: 5 seconds for internal calls.
Transport: cURL (not filegetcontents).
SeoHelper.php
Generates SEO meta tags using templates from ct/dat/seosettings.json.
$seo = SeoHelper::getInstance();
$seo->setPageType('video');
$seo->setPageData([
'title' => $video['title'],
'description' => $video['description'],
'image' => $video['posterurl'],
'duration' => $video['duration'],
]);
echo $seo->getAllMetaTags('video');
Supported page types: video, gallery, model, creator, category, user, generic
Generated tags:
<title>with keyword-optimized template (50-60 chars)<meta name="description">(150-160 chars)- Open Graph tags (
og:title,og:description,og:image,og:type,og:url) - Twitter Card tags (
twitter:card,twitter:title,twitter:description,twitter:image) - Schema.org JSON-LD structured data
- Canonical URL
- Robots meta tag
UrlHelper.php
Generates SEO-friendly URLs and wraps external URLs with click tracking.
UrlHelper::video($id, $title) // /video/123-my-video-title
UrlHelper::gallery($id, $title) // /gallery/456-my-gallery
UrlHelper::model($id, $name) // /model/789-performer-name
UrlHelper::tracked($url, 'video', 123) // click.php?url=...&type=video&id=123
UrlHelper::isEnabled() // Check if SEO URLs active
Slug generation: Removes stopwords, lowercases, replaces spaces with hyphens, max 100 characters.
styleloader.php
Loads custom CSS overrides created via the admin Style Manager (Phase 12). Injects <style> tags after the main stylesheet for theme customization.
bannerhelper.php
Displays ad banners in predefined zones.
displayBannerZone('HEADERTOP');
displayBannerZone('CONTENTMIDDLE');
displayBannerZone('FOOTER');
Supports banner rotation when multiple banners are assigned to one zone.
Multi-Language System (lang/)
Class: Language.php (Singleton pattern)
Language detection priority:
- URL parameter (
?lang=es) - Session variable
- Cookie
- Browser
Accept-Languageheader - Default:
en
25 supported languages:
Arabic, Bengali, Chinese, Danish, English, French, German, Hausa, Hindi, Indonesian, Italian, Japanese, Korean, Marathi, Dutch, Polish, Portuguese, Russian, Spanish, Swahili, Swedish, Telugu, Turkish, Urdu, Vietnamese, Yoruba
Helper functions:
_('key') // Return translated string
('key', ['name' => 'John']) // With variable substitution
e('key') // Echo translated string
en.json structure (500+ keys):
{
"site": { "name": "...", "tagline": "...", "copyright": "..." },
"nav": { "home": "Home", "videos": "Videos", ... },
"footer": { "terms": "Terms & Conditions", ... },
"cookies": { "bannertitle": "We value your privacy", ... },
"common": { "save": "Save", "cancel": "Cancel", "loading": "Loading...", ... },
"home": { "title": "...", "subtitle": "...", "stats": { ... } },
"videos": { "title": "...", "subtitle": "...", "sortby": { ... } },
"galleries": { ... },
"models": { ... },
"cams": { ... },
"auth": { "logintitle": "...", "registertitle": "...", ... },
"creatorprofile": { ... },
"profile": { ... },
"info": { "terms": { ... }, "privacy": { ... }, ... }
}
Root Pages Reference
index.php - Homepage
Purpose: Featured content showcase with statistics dashboard.
Features:
- Dynamic stat cards (total videos, galleries, models, live performers)
- Grid auto-sizes based on enabled features (2 to 4 columns)
- Color-coded cards: green (videos), purple (galleries), pink (models), teal (cams)
- Featured content sections for each enabled content type
API Endpoints:
GET /videos/featured- Trending videosGET /galleries/recent- Recent galleriesGET /models/top- Top modelsGET /cams/online- Live performers
videos.php - Video Browsing
Purpose: Video browsing with search, sorting, and pagination.
Feature guard: featurevideos
Features:
- Search bar with live filtering
- Sort: Newest, Most Viewed, Top Rated, Title A-Z
- Per-page: 52, 104, 156 videos
- 4-column responsive grid
- Subscription content showcase section
- Ad banner zone (CONTENTMIDDLE)
API Endpoints:
GET /videos?page={page}&limit={limit}&sort={sort}&search={search}GET /videos/subscription
videoplayer.php - Video Playback
Purpose: Secure video playback with access control, analytics, and SEO.
Features:
- Server-side SEO data loading (BEFORE header.php for meta tags)
- Access control: free, premium, VIP levels
- Tokenized video/thumbnail URLs
- HLS and MP4 format support
- Related videos playlist
- Player overlays, watermarks, ad overlays
- Star rating system (1-5)
- All external links through
click.php
API Endpoints:
GET /videos/{id}- Video metadataGET /videos/{id}/stats- StatisticsGET /videos/{id}/related- Related videosGET /videos/{id}/access-control- Access check
galleries.php - Gallery Browsing
Purpose: Photo gallery browsing with filters and search.
Feature guard: featuregalleries
Features:
- Search and sort (by ID, clicks, rating, title)
- Per-page: 52, 104, 156
- 4-column grid layout
- Favorites support (logged-in users)
API Endpoints:
GET /galleries?page={page}&limit={limit}&sort={sort}&search={search}
models.php - Model Directory
Purpose: Performer/model directory with type filtering.
Feature guard: featuremodels
Features:
- Model type filter: all, regular, cammodel
- Sort: by ID, popularity, name, video count
- Per-page: 50, 100, 150
- 5-column grid layout (wider cards)
- Search by name
API Endpoints:
GET /models?type={type}&sort={sort}&limit={limit}&search={search}
modeldetails.php - Model Profile
Purpose: Individual model/performer profile with content listings.
camperformers.php - Live Cams Browse
Purpose: Live webcam performer browsing with performance debug tools.
Feature guard: featurelivecams
Features:
- Advanced filters: gender, race, hair, bust, figure, site, HD, age
- Sort: viewers, followers, newest, new models, favorited, experienced, age, HD first, alphabetical
- Per-page: 24, 48
- LIVE badge indicators
- Performance debug panel (
?debug=1) showing: API URL, TTFB, download time, JSON parse time, DOM render time - Favorites support
API Endpoints:
GET /cams/online?page={page}&perpage={perpage}&gender={gender}&sortby={sort}&fast=1GET /cams/sites- Populate site filter
camperformer.php - Cam Detail
Purpose: Individual cam performer profile.
URL: ?id={performerid}
Features:
- Server-side SEO pre-loading
- Online/offline status badge
- Viewer count, HD badge
- External chat link (through
click.php) - Related performers
- Schedule/heatmap
camfilter.php - Cam Filters
Purpose: Filter results page for cam performers.
URL: ?type={filtertype}&value={filtervalue}
Supported types: tag, gender, race, hair, bust, figure, language, site, country, location, pubicarea, isnew, hd, model
categories.php - Category Browsing
Purpose: Browse content categories.
Feature guard: featurecategories
Features:
- Category cards with image/placeholder
- Green gradient placeholders
- Load-more pagination
- Click tracking on category links
users.php - Community Directory
Purpose: Browse community members.
Feature guard: featureprofiles
Features:
- Filters: search, account type (free/premium/vip/moderator/admin), status
- Sort: newest, oldest, most active, alphabetical
- User cards with stats
userprofile.php - User Profile
Purpose: View user profile with content and social features.
URL: ?id={userid}
Features:
- Block checking (redirects if blocked)
- Gradient profile header
- User's videos, playlists, comments
- Video.js integration for embedded player
creator.php - Creator Profile
Purpose: Public creator profile with monetization features.
Feature guard: featurecreatorprofiles
URL: ?username={creatorusername}
Features:
- Creator wall posts (text, image, video, audio, mixed)
- Subscriber count, verification badge
- Follow/Subscribe buttons
- Post interactions (like, comment, unlock)
browsecreators.php - Creator Discovery
Purpose: Creator discovery with filtering and featured section.
Feature guard: featurecreatorprofiles
Features:
- Filters: search, verified status, price range
- Featured creators section
- Creator cards with: avatar, stats, verification badge, pricing
videoplaylist.php - Playlists
Purpose: Video playlist viewer with sequential playback.
livestreams.php - Live Streaming
Purpose: Live stream directory and discovery.
watchstream.php - Stream Viewer
Purpose: Live stream viewer with WebRTC and chat.
Features:
- LiveKit WebRTC connection
- Real-time chat via WebSocket
- Guest user support
- Language detection for chat
click.php - Click Tracking Gateway
Purpose: Universal click tracking and skim enforcement. This is the single most critical file for revenue tracking.
URL format:
click.php?url={encodedurl}&type={type}&id={id}&cat={category}&ref={referrer}
Supported types: video, gallery, model, camperformer, banner, sponsor, internal
What it does:
- Logs click to
tblClickTracking - Sends categories to license server for skim calculation
- Redirects user to destination URL (or skim URL based on license)
- Tracks referrer and content performance
securemedia.php - Tokenized Media
Purpose: Serves media files with tokenized URLs for access control. Prevents hotlinking and unauthorized access.
Filter Endpoints
AJAX endpoints for server-side filtering (used by search/filter components):
Authentication Pages (auth/)
login.php
Features:
- Username/password form
- Password visibility toggle
- 2FA code input (hidden until requested)
- Remember me checkbox
- Forgot password link
- Dark mode support (checks
localStorageimmediately) - Language switcher (English/Spanish)
API: POST /auth/login
{
"username": "string",
"password": "string",
"twofactorcode": "string (optional)",
"rememberme": "boolean"
}
register.php
Feature guard: featureregistration
Features:
- Username (3-50 characters)
- Password with strength indicator
- Confirm password
- Terms acceptance checkbox
- Age verification (18+)
- Email verification required after registration
API: POST /auth/register
logout.php
Clears session and redirects to homepage.
setsession.php
Sets PHP session data after successful API login. Called after POST /auth/login returns a JWT token.
User Settings (settings/)
All settings pages require authentication and redirect to /auth/login.php if not logged in.
Core Settings
Creator Settings
profile.php Tab System
The main profile page uses a tabbed interface:
Information Pages (information/)
Static/legal pages following a consistent layout using .info-page, .info-container, .info-section classes. All links routed through click.php.
Installation Wizard (install/)
A 7-stage installation wizard at /install/index.php:
Session-based progress tracking with stage-by-stage validation.
Mandatory Patterns
Click Tracking (click.php)
Every outbound link MUST use click.php. No exceptions.
// PHP
$url = sprintf('click.php?url=%s&type=video&id=%d',
urlencode($externalurl), $videoid);
echo '<a href="' . htmlspecialchars($url) . '" target="blank">';
// JavaScript
const url = click.php?url=${encodeURIComponent(externalUrl)}&type=${type}&id=${id};
window.open(url, 'blank');
Translation System
Never hardcode user-facing text.
// Correct
<h1><?= ('videos.title') ?></h1>
<p><?= ('videos.subtitle') ?></p>
// Wrong
<h1>Videos</h1>
<p>Browse our collection</p>
Variable substitution:
<?= ('profile.welcome', ['name' => $username]) ?>
// en.json: "welcome": "Welcome back, {name}!"
Feature Guards
Check feature toggles at the top of every page:
if (!isfeatureenabled('featurevideos')) {
featuredisabledredirect(_('nav.videos'));
}
API-First Data Access
Frontend pages never query the database. All data comes from the REST API.
// Client-side (JavaScript)
const api = new ApiClient();
const response = await api.get('/videos', { page: 1, limit: 20 });
// Server-side (PHP - for SEO pre-loading only)
$api = InternalApiHelper::getInstance();
$data = $api->get('/videos/' . $videoId);
SEO Pre-Loading
Pages that need SEO meta tags (videoplayer, camperformer, modeldetails) load data BEFORE including header.php:
// 1. Load config and API helper
requireonce DIR . '/ct/dat/config.inc.php';
requireonce DIR . '/includes/InternalApiHelper.php';
// 2. Fetch data for SEO
$api = InternalApiHelper::getInstance();
$videoData = $api->getVideoForSeo($videoId);
// 3. NOW include header (which generates meta tags from loaded data)
requireonce DIR . '/includes/header.php';
Troubleshooting
Blank page / 500 error
declare(stricttypes=1) must be the FIRST line after <?php in any file that uses it. Check that no BOM or whitespace precedes it.
Features not showing / all disabled
- Check that the API is reachable:
curl https://domain/ct/api/v1/features - Verify feature cache hasn't stale-locked: clear APCu cache
- Check
featurehelper.phpdefault values (all default to enabled) - Verify feature settings in admin panel
Translations not loading / showing keys instead of text
Verify
lang/en.json exists and is valid JSONCheck file permissions (readable by web server)Verify the translation key exists in the JSON file
- Check for typos in nested keys (e.g.,
videos.sortby.videoidrequires all parent keys to exist)
Dark mode not working
- Check
featuredarkmodeis enabled in feature toggles - Verify
localStorageis available (not blocked by browser) - Confirm
body.dark-modeCSS rules exist in style.css
CSS styles not applying
- Only use
assets/css/style.css- no external CSS frameworks allowed - Check specificity conflicts with Style Manager overrides
- Verify the stylesheet is loaded in header.php
- Check browser cache (append
?v=timestampto stylesheet URL)
API calls failing from JavaScript
Check
ApiClient base URL detection: open browser console and run new ApiClient().baseUrlVerify CORS headers are set if API is on different domain
- Check JWT token in
localStorage.getItem('authtoken') - Verify the endpoint exists in
ct/api/v1/index.phprouter
Video player not loading
- Verify video data loads: check browser Network tab for
/ct/api/v1/videos/{id} - Check access control: user may not have permission
- Verify HLS.js or native HLS support for
.m3u8files - Check
secure_media.phptoken generation