Project WordPress Content Draft_Manager_CRS_V1

**Current Date and Time: Friday, June 13, 2025, 5:08 PM GMT-5 (Panama City/Bogotá)**

# **Code Repository Status Template (CRS) – Content Draft Manager V2**

## **Version Status Overview**

– **Working On:** Phase 1 Core – Bug fixes and optimization pending
– **Current/Production:** Phase 1 Implementation – 85% functional with known issues requiring resolution

## **Working Version Details**

**Status:** Active development – core functionality operational with identified bugs
**Changes:**
– Enhanced Tab 3 with Claude AI integration complete
– IMDB data fetching with cURL implementation
– YouTube transcript fetching via Python script integration
– Auto-save functionality across all form sections
– 5-step workflow system implemented
– AJAX handlers for all major functions
– Settings page with API key management

**Last Modified:** June 13, 2025

**Active Debugging:**

– **Issue:** Tab navigation skipping from Tab 1 to Tab 3 instead of sequential progression
– **Suspected Cause:** JavaScript event handler conflicts in admin.js navigation logic
– **Attempts:** Navigation logic identified, requires event handler deduplication
– **Server Impact:** No performance impact – UI navigation issue only

– **Issue:** Access meta box still visible despite removal attempts
– **Suspected Cause:** Incomplete meta box removal logic in class-draft-manager.php
– **Attempts:** Enhanced hideaccessmetabox method needs global $wp_metaboxes manipulation
– **Server Impact:** Visual inconsistency only, no functional impact

– **Issue:** Dashboard layout alignment inconsistencies
– **Suspected Cause:** CSS grid alignment issues in admin.css
– **Attempts:** Requires align-items: start specification for proper grid layout
– **Server Impact:** Visual presentation only

**Code File Order:**

### Plugin-File-Structure.txt
content-draft-manager/
├── content-draft-manager.php (Main plugin file)
├── admin/
│ ├── dashboard.php (Dashboard template)
│ └── settings.php (Settings page)

├── assets/
│ ├── css/
│ │ └── admin.css (Admin styles)
│ └── js/
│ └── admin.js (Admin JavaScript)
└── includes/
│ ├── class-database.php (Database operations)
│ ├── class-draft-form.php (Form rendering)
│ ├── class-draft-manager.php (Main manager class)
│ ├── class-groq-ai-detector.php (AI detection)
│ ├── class-imdb-fetcher.php (IMDB scraping)
│ ├── class-plagiarism-detector.php (Plagiarism checking)
│ ├── class-tmdb-fetcher.php (TMDB API Integration)
│ └── extract_transcripts.py (YouTube Transcript Fetcher Python Script)
└── temp

### content-draft-manager.php
✅ Plugin initialization and hook registration complete

<?php
/**
* Plugin Name: Content Draft Manager
* Description: Advanced draft management system for movie/TV content creation
* Version: 1.0.3
* Author: Your Name
* Text Domain: content-draft-manager
*/

// Prevent direct access
if (!defined(‘ABSPATH’)) {
exit;
}

// Define plugin constants
define(‘CDM_PLUGIN_URL’, plugin_dir_url(__FILE__));
define(‘CDM_PLUGIN_PATH’, plugin_dir_path(__FILE__));
define(‘CDM_VERSION’, ‘1.0.3’);

// Simple file loading with absolute paths
$plugin_dir = dirname(__FILE__);

// Load files in correct order
require_once $plugin_dir . ‘/includes/class-database.php’;
require_once $plugin_dir . ‘/includes/class-imdb-fetcher.php’;
require_once $plugin_dir . ‘/includes/class-plagiarism-detector.php’;
require_once $plugin_dir . ‘/includes/class-groq-ai-detector.php’;
require_once $plugin_dir . ‘/includes/class-draft-form.php’;
require_once $plugin_dir . ‘/includes/class-draft-manager.php’;
require_once $plugin_dir . ‘/includes/class-tmdb-fetcher.php’;

// Initialize the plugin
function cdm_init() {
CDM_Draft_Manager::get_instance();
}
add_action(‘plugins_loaded’, ‘cdm_init’);

// Activation hook
register_activation_hook(__FILE__, ‘cdm_activate’);
function cdm_activate() {
CDM_Database::create_tables();
flush_rewrite_rules();
}

// Deactivation hook
register_deactivation_hook(__FILE__, ‘cdm_deactivate’);
function cdm_deactivate() {
flush_rewrite_rules();
}
?>

### class-draft-manager.php
✅ AJAX handlers operational with security validation
⚠️ Meta box removal logic needs enhancement

<?php
/**
* Content Draft Manager Main Class – Fixed YouTube Transcript Integration
* Last Update: June 09, 2025 at 04:05 AM GMT – Fixed AJAX handler registration and parameter names
* Purpose: Main plugin class with working YouTube transcript fetching
* Features: IMDB fetching, streaming platforms, YouTube transcripts, auto-save
*/

class CDM_Draft_Manager {
private static $instance = null;
private static $hooks_registered = false;

public static function get_instance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}

private function __construct() {
error_log(‘CDM Constructor call – Registering hooks’);

add_action(‘init’, array($this, ‘init’));
add_action(‘admin_menu’, array($this, ‘add_admin_menu’));
add_action(‘admin_enqueue_scripts’, array($this, ‘enqueue_admin_scripts’));

// AJAX handlers – SINGLE REGISTRATION with static protection
static $ajax_registered = false;
if (!$ajax_registered) {
error_log(‘CDM Registering AJAX handlers’);
add_action(‘wp_ajax_cdm_auto_save’, array($this, ‘ajax_auto_save’));
add_action(‘wp_ajax_cdm_fetch_imdb’, array($this, ‘ajax_fetch_imdb’));
add_action(‘wp_ajax_cdm_fetch_trailer’, array($this, ‘ajax_fetch_trailer’));
add_action(‘wp_ajax_cdm_fetch_streaming’, array($this, ‘ajax_fetch_streaming’));
add_action(‘wp_ajax_cdm_fetch_transcripts’, array($this, ‘ajax_fetch_transcripts’)); // FIXED: Added missing handler
add_action(‘wp_ajax_cdm_claude_generate’, array($this, ‘ajax_claude_generate’));
$ajax_registered = true;
}

add_action(‘save_post’, array($this, ‘save_draft_meta’));
add_action(‘add_meta_boxes’, array($this, ‘customize_meta_boxes’), 999);
add_action(‘admin_head’, array($this, ‘hide_access_meta_box’));

// FIXED: Enhanced save validation without duplication
add_filter(‘wp_insert_post_data’, array($this, ‘fix_draft_save_validation’), 10, 2);

// PREVENT: Duplicate post creation
add_action(‘wp_insert_post’, array($this, ‘prevent_duplicate_posts’), 10, 3);

error_log(‘CDM All hooks registered successfully’);
}

public function init() {
$this->create_post_type();
$this->add_meta_boxes();
}

public function create_post_type() {
register_post_type(‘content_draft’, array(
‘labels’ => array(
‘name’ => ‘Content Drafts’,
‘singular_name’ => ‘Content Draft’,
‘add_new’ => ‘Add New Draft’,
‘add_new_item’ => ‘Add New Content Draft’,
‘edit_item’ => ‘Edit Content Draft’,
‘new_item’ => ‘New Content Draft’,
‘view_item’ => ‘View Content Draft’,
‘search_items’ => ‘Search Content Drafts’,
‘not_found’ => ‘No content drafts found’,
‘not_found_in_trash’ => ‘No content drafts found in trash’
),
‘public’ => true,
‘show_ui’ => true,
‘show_in_menu’ => false,
‘supports’ => array(‘title’, ‘custom-fields’),
‘capability_type’ => ‘post’,
‘has_archive’ => false,
‘publicly_queryable’ => false,
‘show_in_rest’ => true,
‘menu_icon’ => ‘dashicons-edit-page’,
‘taxonomies’ => array(‘category’)
));
}

// NEW: Prevent duplicate post creation based on search results
public function prevent_duplicate_posts($post_id, $post, $update) {
// Only apply to our custom post type
if ($post->post_type !== ‘content_draft’) {
return;
}

// Skip if this is an update
if ($update) {
return;
}

// Check for existing posts with similar titles
$movie_title = get_post_meta($post_id, ‘cdm_movie_title’, true);
if (!empty($movie_title)) {
$existing_posts = get_posts(array(
‘post_type’ => ‘content_draft’,
‘meta_query’ => array(
array(
‘key’ => ‘cdm_movie_title’,
‘value’ => $movie_title,
‘compare’ => ‘=’
)
),
‘post_status’ => array(‘draft’, ‘publish’),
‘exclude’ => array($post_id)
));

// If duplicate found, delete this new post and redirect to existing
if (!empty($existing_posts)) {
$existing_post = $existing_posts[0];

// Delete the duplicate
wp_delete_post($post_id, true);

// Redirect to existing post
if (is_admin()) {
wp_redirect(admin_url(‘post.php?post=’ . $existing_post->ID . ‘&action=edit’));
exit;
}
}
}
}

public function add_meta_boxes() {
add_action(‘add_meta_boxes’, function() {
add_meta_box(
‘cdm_draft_form’,
‘Draft Content Form’,
array($this, ‘render_meta_box’),
‘content_draft’,
‘normal’,
‘high’
);
});
}

public function customize_meta_boxes() {
remove_meta_box(‘pageparentdiv’, ‘content_draft’, ‘side’);
remove_meta_box(‘authordiv’, ‘content_draft’, ‘normal’);
remove_meta_box(‘slugdiv’, ‘content_draft’, ‘normal’);
remove_meta_box(‘postcustom’, ‘content_draft’, ‘normal’);
remove_meta_box(‘commentstatusdiv’, ‘content_draft’, ‘normal’);
remove_meta_box(‘commentsdiv’, ‘content_draft’, ‘normal’);
remove_meta_box(‘trackbacksdiv’, ‘content_draft’, ‘normal’);
remove_meta_box(‘revisionsdiv’, ‘content_draft’, ‘normal’);
remove_meta_box(‘formatdiv’, ‘content_draft’, ‘side’);
remove_meta_box(‘tagsdiv-post_tag’, ‘content_draft’, ‘side’);

add_meta_box(
‘categorydiv’,
‘Categories’,
‘post_categories_meta_box’,
‘content_draft’,
‘side’,
‘core’
);
}

public function hide_access_meta_box() {
global $post_type;
if ($post_type === ‘content_draft’) {
echo ‘<style>
#pageparentdiv, #authordiv, #slugdiv, #postcustom, #commentstatusdiv,
#commentsdiv, #trackbacksdiv, #revisionsdiv, #formatdiv, #tagsdiv-post_tag,
.postbox[id*=”access”], .postbox[id*=”parent”] { display: none !important; }
</style>’;
}
}

public function render_meta_box($post) {
wp_nonce_field(‘cdm_save_draft_meta’, ‘cdm_meta_nonce’);

$form = new CDM_Draft_Form($post->ID, $this); // FIXED: Pass required parameters
$form->render(); // FIXED: No need to pass post_id again
}

public function add_admin_menu() {
add_menu_page(
‘Draft Manager’,
‘Draft Manager’,
‘manage_options’,
‘draft-manager’,
array($this, ‘admin_dashboard’),
‘dashicons-edit-page’,
25
);

add_submenu_page(
‘draft-manager’,
‘All Drafts’,
‘All Drafts’,
‘manage_options’,
‘edit.php?post_type=content_draft’
);

add_submenu_page(
‘draft-manager’,
‘Add New Draft’,
‘Add New Draft’,
‘manage_options’,
‘post-new.php?post_type=content_draft’
);

add_submenu_page(
‘draft-manager’,
‘Settings’,
‘Settings’,
‘manage_options’,
‘draft-manager-settings’,
array($this, ‘settings_page’)
);
}

public function admin_dashboard() {
include CDM_PLUGIN_PATH . ‘admin/dashboard.php’;
}

public function settings_page() {
include CDM_PLUGIN_PATH . ‘admin/settings.php’;
}

public function enqueue_admin_scripts($hook) {
global $post_type;

if ($post_type === ‘content_draft’ || strpos($hook, ‘draft-manager’) !== false) {
wp_enqueue_script(
‘cdm-admin-js’,
CDM_PLUGIN_URL . ‘assets/js/admin.js’,
array(‘jquery’),
CDM_VERSION,
true
);

wp_enqueue_style(
‘cdm-admin-css’,
CDM_PLUGIN_URL . ‘assets/css/admin.css’,
array(),
time(),
‘all’
);

wp_localize_script(‘cdm-admin-js’, ‘cdmajax’, array(
‘ajaxurl’ => admin_url(‘admin-ajax.php’),
‘nonce’ => wp_create_nonce(‘cdm_nonce’),
‘autosave_interval’ => 30000
));
}
}

// FIXED: Enhanced save validation without creating duplicates
public function fix_draft_save_validation($data, $postarr) {
// Only apply to our custom post type
if (isset($postarr[‘post_type’]) && $postarr[‘post_type’] === ‘content_draft’) {
// FIXED: Don’t change title if it already exists and has movie data
if (empty($data[‘post_title’]) || $data[‘post_title’] === ‘Auto Draft’) {
// Try to get movie title from meta first
if (!empty($postarr[‘ID’])) {
$movie_title = get_post_meta($postarr[‘ID’], ‘cdm_movie_title’, true);
if ($movie_title) {
$data[‘post_title’] = $movie_title . ‘ (‘ . get_post_meta($postarr[‘ID’], ‘cdm_movie_year’, true) . ‘) – Movie Draft’;
} else {
$data[‘post_title’] = ‘Movie Draft – ‘ . date(‘Y-m-d H:i:s’);
}
} else {
$data[‘post_title’] = ‘New Movie Draft – ‘ . date(‘Y-m-d H:i:s’);
}
}

// Ensure we have content
if (empty($data[‘post_content’])) {
$data[‘post_content’] = ‘Content draft created via Draft Manager plugin on ‘ . date(‘Y-m-d H:i:s’) . ‘.’;
}

// Ensure proper post status
if (empty($data[‘post_status’])) {
$data[‘post_status’] = ‘auto-draft’;
}
if ($data[‘post_status’] === ‘auto-draft’) {
$data[‘post_status’] = ‘draft’;
}

// Ensure we have an author
if (empty($data[‘post_author’])) {
$data[‘post_author’] = get_current_user_id();
}
}

return $data;
}

public function ajax_auto_save() {
// Enhanced validation for auto-save to prevent “Missing required data” errors
if (!wp_verify_nonce($_POST[‘nonce’] ?? ”, ‘cdm_nonce’)) {
wp_send_json_error(‘Security check failed’);
return;
}

$post_id = intval($_POST[‘post_id’] ?? 0);
if (empty($post_id)) {
// Silently fail for auto-save when no post ID – this is normal
wp_send_json_success(‘Auto-save skipped – no post ID’);
return;
}

// Check if this is a WordPress auto-save (happens every 60 seconds)
if (defined(‘DOING_AUTOSAVE’) && DOING_AUTOSAVE) {
// For auto-save, only save if we have meaningful content
$title = sanitize_text_field($_POST[‘post_title’] ?? ”);
$content = wp_kses_post($_POST[‘post_content’] ?? ”);

if (empty($title) && empty($content)) {
// Silently succeed for empty auto-saves
wp_send_json_success(‘Auto-save skipped – no content’);
return;
}
}

$section = isset($_POST[‘section’]) ? sanitize_text_field($_POST[‘section’]) : ”;
$content = isset($_POST[‘content’]) ? $_POST[‘content’] : ”;

// Enhanced validation – allow empty section for background saves
if (!$post_id) {
wp_send_json_error(‘Missing required data’);
return;
}

if (!current_user_can(‘edit_post’, $post_id)) {
wp_send_json_error(‘Permission denied’);
return;
}

// If we have section data, save it normally
if (!empty($section)) {
$sanitized_content = $this->sanitize_section_content($section, $content);
update_post_meta($post_id, ‘cdm_’ . $section, $sanitized_content);
}

// Always succeed for auto-save to prevent error notifications
wp_send_json_success(array(
‘message’ => ‘Auto-saved successfully’,
‘timestamp’ => current_time(‘H:i:s’)
));
}

private function sanitize_section_content($section, $content) {
$youtube_allowed_tags = array(
‘iframe’ => array(
‘src’ => array(),
‘width’ => array(),
‘height’ => array(),
‘frameborder’ => array(),
‘allow’ => array(),
‘allowfullscreen’ => array(),
‘title’ => array(),
‘referrerpolicy’ => array(),
‘style’ => array(),
‘class’ => array(),
‘id’ => array()
)
);

switch ($section) {
case ‘youtube_embed’:
return wp_kses($content, $youtube_allowed_tags);
case ‘research_data’:
case ‘prompt’:
case ‘ai_article’:
case ‘translation’:
case ‘final_edit’:
case ‘generated_draft’: // ADD THIS
case ‘personal_note’: // ADD THIS
return wp_kses_post($content);
case ‘sources’:
case ‘generated_titles’:
case ‘hashtags’:
case ‘claude_model’: // ADD THIS
return sanitize_textarea_field($content);
default:
return filter_var($content, FILTER_VALIDATE_URL) ? esc_url_raw($content) : sanitize_text_field($content);
}
}

public function save_draft_meta($post_id) {
if (defined(‘DOING_AUTOSAVE’) && DOING_AUTOSAVE) {
return;
}

if (!isset($_POST[‘cdm_meta_nonce’]) || !wp_verify_nonce($_POST[‘cdm_meta_nonce’], ‘cdm_save_draft_meta’)) {
return;
}

if (!current_user_can(‘edit_post’, $post_id)) {
return;
}

if (get_post_type($post_id) !== ‘content_draft’) {
return;
}

$sections = array(‘imdb_url’, ‘youtube_embed’, ‘streaming_platform’, ‘sources’, ‘research_data’, ‘prompt’, ‘generated_titles’, ‘hashtags’, ‘ai_article’, ‘translation’, ‘final_edit’);

foreach ($sections as $section) {
if (isset($_POST[‘cdm_’ . $section])) {
$content = $_POST[‘cdm_’ . $section];
$sanitized_content = $this->sanitize_section_content($section, $content);
update_post_meta($post_id, ‘cdm_’ . $section, $sanitized_content);
}
}
}

// ENHANCED IMDB AJAX HANDLER – NO DUPLICATION
public function ajax_fetch_imdb() {
// BULLETPROOF: Enhanced transient-based deduplication
$post_id = isset($_POST[‘post_id’]) ? intval($_POST[‘post_id’]) : 0; // FIXED: Changed from postid to post_id
$imdb_url = isset($_POST[‘imdb_url’]) ? sanitize_url($_POST[‘imdb_url’]) : ”;
$user_id = get_current_user_id();
$nonce = $_POST[‘nonce’] ?? ”;

// Create ultra-specific signature
$request_signature = md5($post_id . ‘|’ . $imdb_url . ‘|’ . $user_id . ‘|’ . $nonce . ‘|’ . time());
$processing_key = ‘cdm_processing_’ . substr($request_signature, 0, 16);
$global_processing_key = ‘cdm_global_processing_’ . $user_id;

// Check if ANY similar request is being processed…
if (get_transient($global_processing_key)) {
error_log(‘CDM: Global processing active – blocking request’);
wp_send_json_error(‘Another IMDB request is already being processed’);
return;
}

// Set global processing flag
set_transient($global_processing_key, true, 30);
set_transient($processing_key, true, 60);

// DEBUG: Log all received data
error_log(‘=== CDM AJAX DEBUG ===’);
error_log(‘POST data: ‘ . print_r($_POST, true));
error_log(‘Received post_id: ‘ . ($_POST[‘post_id’] ?? ‘NOT SET’));
error_log(‘Nonce check: ‘ . (wp_verify_nonce($_POST[‘nonce’] ?? ”, ‘cdm_nonce’) ? ‘PASS’ : ‘FAIL’));

if (!wp_verify_nonce($_POST[‘nonce’], ‘cdm_nonce’)) {
error_log(‘CDM: Nonce verification failed’);
delete_transient($global_processing_key);
delete_transient($processing_key);
wp_send_json_error(‘Security check failed’);
return;
}

error_log(‘CDM: Sanitized post_id: ‘ . $post_id);
error_log(‘CDM: IMDB URL: ‘ . $imdb_url);

if (empty($imdb_url)) {
delete_transient($global_processing_key);
delete_transient($processing_key);
wp_send_json_error(‘IMDB URL is required’);
return;
}

// Validate Post ID
if (empty($post_id) || $post_id == 0) {
error_log(‘CDM: Post ID validation failed – post_id is ‘ . var_export($post_id, true));
delete_transient($global_processing_key);
delete_transient($processing_key);
wp_send_json_error(‘Post ID is required’);
return;
}

// Validate IMDB URL format
if (!preg_match(‘/imdb\.com\/title\/tt/’, $imdb_url)) {
error_log(‘CDM ERROR – Invalid IMDB URL format: ‘ . $imdb_url);
delete_transient($global_processing_key);
delete_transient($processing_key);
wp_send_json_error(‘Invalid IMDB URL format’);
return;
}

// Check user permissions
if (!current_user_can(‘edit_post’, $post_id)) {
error_log(‘CDM ERROR – Permission denied for post_id: ‘ . $post_id);
delete_transient($global_processing_key);
delete_transient($processing_key);
wp_send_json_error(‘Permission denied’);
return;
}

// Save IMDB URL to post meta
update_post_meta($post_id, ‘cdm_imdb_url’, $imdb_url);
error_log(‘CDM: Saved IMDB URL to post meta for post ID: ‘ . $post_id);

// Fetch movie data
error_log(‘CDM: Starting IMDB data fetch…’);
$movie_data = CDM_IMDB_Fetcher::fetch_movie_data($imdb_url, $post_id);

if (isset($movie_data[‘error’])) {
error_log(‘CDM: IMDB Fetcher returned error: ‘ . $movie_data[‘error’]);
delete_transient($global_processing_key);
delete_transient($processing_key);
wp_send_json_error($movie_data[‘error’]);
return;
}

error_log(‘CDM: IMDB data fetch successful’);
error_log(‘CDM: Movie title: ‘ . ($movie_data[‘title’] ?? ‘No title’));
error_log(‘=== CDM AJAX FETCH IMDB END ===’);

delete_transient($global_processing_key);
delete_transient($processing_key);
wp_send_json_success($movie_data);
}

public function ajax_fetch_trailer() {
if (!wp_verify_nonce($_POST[‘nonce’], ‘cdm_nonce’)) {
wp_send_json_error(‘Security check failed’);
return;
}

$post_id = isset($_POST[‘post_id’]) ? intval($_POST[‘post_id’]) : 0;
$language = isset($_POST[‘language’]) ? sanitize_text_field($_POST[‘language’]) : ‘en’;

if (empty($post_id)) {
wp_send_json_error(‘Post ID is required’);
return;
}

if (!current_user_can(‘edit_post’, $post_id)) {
wp_send_json_error(‘Permission denied’);
return;
}

$allowed_languages = array(‘en’, ‘es’);
if (!in_array($language, $allowed_languages)) {
$language = ‘en’;
}

$movie_data = CDM_IMDB_Fetcher::get_saved_movie_data($post_id);
if (empty($movie_data[‘title’])) {
wp_send_json_error(‘No movie data found. Please fetch IMDB data first.’);
return;
}

$api_key = get_option(‘cdm_youtube_api_key’);
if (empty($api_key)) {
wp_send_json_error(‘YouTube API key not configured’);
return;
}

$search_query = $movie_data[‘title’];
if (!empty($movie_data[‘year’])) {
$search_query .= ‘ ‘ . $movie_data[‘year’];
}

if ($language === ‘es’) {
$search_query .= ‘ trailer oficial’;
} else {
$search_query .= ‘ official trailer’;
}

$api_url = ‘https://www.googleapis.com/youtube/v3/search?’ . http_build_query(array(
‘part’ => ‘snippet’,
‘q’ => $search_query,
‘type’ => ‘video’,
‘maxResults’ => 1,
‘key’ => $api_key
));

$response = wp_remote_get($api_url);
if (is_wp_error($response)) {
wp_send_json_error(‘YouTube API request failed’);
return;
}

$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);

if (isset($data[‘items’][0])) {
$video = $data[‘items’][0];
$video_id = $video[‘id’][‘videoId’];

$embed_code = ‘<iframe width=”560″ height=”315″ src=”https://www.youtube.com/embed/’ . $video_id . ‘” frameborder=”0″ allowfullscreen></iframe>’;

update_post_meta($post_id, ‘cdm_youtube_embed’, $embed_code);

wp_send_json_success(array(
‘video_id’ => $video_id,
‘title’ => $video[‘snippet’][‘title’],
’embed_code’ => $embed_code,
‘search_query’ => $search_query,
‘language’ => $language
));
} else {
wp_send_json_error(‘No trailer found for: ‘ . $search_query);
}
}

/**
* AJAX Fetch Streaming Platform
* Last Update: June 08, 2025 12:30 AM GMT – Added comprehensive debugging and error handling
* Purpose: Fetches streaming platform URLs using IMDB ID
* Features: Auto-fills streaming platform field with actual URLs
* API: Streaming Availability API via RapidAPI
*/
public function ajax_fetch_streaming() {
// Security check
if (!wp_verify_nonce($_POST[‘nonce’], ‘cdm_nonce’)) {
wp_send_json_error(‘Security check failed’);
return;
}

// Get post ID
$post_id = isset($_POST[‘post_id’]) ? intval($_POST[‘post_id’]) : 0;
if (empty($post_id)) {
wp_send_json_error(‘Post ID is required’);
return;
}

// Check permissions
if (!current_user_can(‘edit_post’, $post_id)) {
wp_send_json_error(‘Permission denied’);
return;
}

// Get API key
$api_key = get_option(‘cdm_streaming_api_key’);
if (empty($api_key)) {
wp_send_json_error(‘Streaming Availability API key not configured’);
return;
}

// Get IMDB ID from saved movie data
$imdb_url = get_post_meta($post_id, ‘cdm_imdb_url’, true);
if (empty($imdb_url)) {
wp_send_json_error(‘No IMDB data found. Please fetch IMDB data first.’);
return;
}

// Extract IMDB ID
preg_match(‘/title\/(tt\d+)/’, $imdb_url, $matches);
if (!$matches) {
wp_send_json_error(‘Invalid IMDB URL format’);
return;
}
$imdb_id = $matches[1];

// Call Streaming Availability API
$api_url = ‘https://streaming-availability.p.rapidapi.com/shows/’ . $imdb_id . ‘?country=us’;

$response = wp_remote_get($api_url, array(
‘headers’ => array(
‘X-RapidAPI-Key’ => $api_key,
‘X-RapidAPI-Host’ => ‘streaming-availability.p.rapidapi.com’
),
‘timeout’ => 15
));

if (is_wp_error($response)) {
wp_send_json_error(‘Failed to fetch streaming data: ‘ . $response->get_error_message());
return;
}

$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);

// ADD DEBUG CODE HERE
error_log(‘CDM: Raw API response body: ‘ . $body);
error_log(‘CDM: Decoded API data: ‘ . print_r($data, true));

if (isset($data[‘error’])) {
wp_send_json_error(‘Streaming API Error: ‘ . $data[‘error’][‘message’]);
return;
}

// Process streaming data
$streaming_info = $this->process_streaming_data($data);

if (empty($streaming_info[‘platforms’])) {
wp_send_json_error(‘No streaming platforms found for this content’);
return;
}

// Save the priority streaming URL to the field
$priority_url = $streaming_info[‘priority_url’];
update_post_meta($post_id, ‘cdm_streaming_platform’, $priority_url);

// Save free options display permanently
if (!empty($streaming_info[‘free_display’])) {
update_post_meta($post_id, ‘cdm_free_streaming_display’, $streaming_info[‘free_display’]);
}

wp_send_json_success(array(
‘priority_url’ => $priority_url,
‘platforms’ => $streaming_info[‘platforms’],
‘free_platforms’ => $streaming_info[‘free_platforms’],
‘total_platforms’ => count($streaming_info[‘platforms’]),
‘free_display’ => $streaming_info[‘free_display’]
));
}

/**
* AJAX Fetch YouTube Transcripts
* Last Update: June 09, 2025 at 04:05 AM GMT – Fixed parameter names and data field
* Purpose: Fetches transcripts from YouTube videos and saves to Research Data
* Features: Multi-language support, error handling, data persistence
* @param {Array} urls – Array of YouTube URLs from form
*/
public function ajax_fetch_transcripts() {
// Security check
if (!wp_verify_nonce($_POST[‘nonce’], ‘cdm_nonce’)) {
wp_send_json_error(‘Security check failed’);
return;
}

$post_id = isset($_POST[‘post_id’]) ? intval($_POST[‘post_id’]) : 0; // FIXED: Changed from postid to post_id
$urls = isset($_POST[‘urls’]) ? sanitize_textarea_field($_POST[‘urls’]) : ”;
$language_preference = isset($_POST[‘language_preference’]) ? sanitize_text_field($_POST[‘language_preference’]) : ‘auto’;

if (empty($post_id)) {
wp_send_json_error(‘Post ID is required’);
return;
}

if (!current_user_can(‘edit_post’, $post_id)) {
wp_send_json_error(‘Permission denied’);
return;
}

if (empty($urls)) {
wp_send_json_error(‘No YouTube URLs provided’);
return;
}

// Parse URLs (one per line)
$url_array = array_filter(array_map(‘trim’, explode(“\n”, $urls)));

if (count($url_array) > 5) {
wp_send_json_error(‘Maximum 5 URLs allowed’);
return;
}

// Validate YouTube URLs
foreach ($url_array as $url) {
if (!preg_match(‘/youtube\.com\/watch\?v=|youtu\.be\//’, $url)) {
wp_send_json_error(‘Invalid YouTube URL: ‘ . $url);
return;
}
}

// Create temporary file with URLs
$temp_file = tempnam(sys_get_temp_dir(), ‘youtube_urls_’);
file_put_contents($temp_file, implode(“\n”, $url_array));

// Path to Python script
$python_script = CDM_PLUGIN_PATH . ‘includes/extract_transcripts.py’;

// Execute Python script with language preference
$command = “python3 $python_script $temp_file $language_preference 2>&1″;
$output = shell_exec($command);

// Read the generated transcript file
$transcript_file = dirname($temp_file) . ‘/all_transcripts.txt’;
$transcripts = ”;

if (file_exists($transcript_file)) {
$transcripts = file_get_contents($transcript_file);
unlink($transcript_file);
}

// Clean up temp file
unlink($temp_file);

if (!empty($transcripts)) {
// Get existing research data
$existing_research_data = get_post_meta($post_id, ‘cdm_research_data’, true); // FIXED: Changed from cdm_researchdata to cdm_research_data

// Append transcripts to existing research data
$separator = !empty($existing_research_data) ? “\n\n=== YOUTUBE TRANSCRIPTS ===\n\n” : “”;
$updated_research_data = $existing_research_data . $separator . $transcripts;

// Save to post meta
update_post_meta($post_id, ‘cdm_research_data’, $updated_research_data); // FIXED: Changed from cdm_researchdata to cdm_research_data

wp_send_json_success(array(
‘transcripts’ => $transcripts,
‘updated_research_data’ => $updated_research_data,
‘debug’ => $output
));
} else {
wp_send_json_error(‘Failed to fetch transcripts. Debug: ‘ . $output);
}
}
// AJAX Fetch YouTube Transcripts – Ends Here

/**
* Claude AI Content Generation Handler
* Last Update: June 09, 2025 – Phase 1 Implementation
* Purpose: Handles Claude API calls for content generation
* Features: Cost calculation, response parsing, error handling
*/
public function ajax_claude_generate() {
// Security check
if (!wp_verify_nonce($_POST[‘nonce’], ‘cdm_nonce’)) {
wp_send_json_error(‘Security check failed’);
return;
}

$post_id = isset($_POST[‘post_id’]) ? intval($_POST[‘post_id’]) : 0;
$model = isset($_POST[‘model’]) ? sanitize_text_field($_POST[‘model’]) : ‘claude-3-5-sonnet-20241022’;
$personal_note = isset($_POST[‘personal_note’]) ? sanitize_textarea_field($_POST[‘personal_note’]) : ”;
$research_data = isset($_POST[‘research_data’]) ? wp_kses_post($_POST[‘research_data’]) : ”;

if (empty($post_id)) {
wp_send_json_error(‘Post ID is required’);
return;
}

if (!current_user_can(‘edit_post’, $post_id)) {
wp_send_json_error(‘Permission denied’);
return;
}

if (empty($research_data)) {
wp_send_json_error(‘Research data is required for AI generation’);
return;
}

// Get Claude API key from settings
$api_key = get_option(‘cdm_claude_api_key’);
if (empty($api_key)) {
wp_send_json_error(‘Claude API key not configured. Please add it in Settings.’);
return;
}

// Get movie data for context
$movie_data = CDM_IMDB_Fetcher::get_saved_movie_data($post_id);
$movie_title = !empty($movie_data[‘title’]) ? $movie_data[‘title’] : ‘Unknown Movie’;

// Build the prompt
$prompt = $this->build_claude_prompt($movie_title, $research_data, $personal_note, $movie_data);

// Make Claude API call
$claude_response = $this->call_claude_api($api_key, $model, $prompt);

if (is_wp_error($claude_response)) {
wp_send_json_error(‘Claude API error: ‘ . $claude_response->get_error_message());
return;
}

// Parse Claude response
$parsed_content = $this->parse_claude_response($claude_response[‘content’]);

// Save generated content
if (!empty($parsed_content[‘titles’])) {
update_post_meta($post_id, ‘cdm_generated_titles’, $parsed_content[‘titles’]);
}

if (!empty($parsed_content[‘hashtags’])) {
update_post_meta($post_id, ‘cdm_hashtags’, $parsed_content[‘hashtags’]);
}

if (!empty($parsed_content[‘article’])) {
update_post_meta($post_id, ‘cdm_generated_draft’, $parsed_content[‘article’]);
}

// Calculate actual cost
$tokens_used = $claude_response[‘usage’][‘input_tokens’] + $claude_response[‘usage’][‘output_tokens’];
$cost = $this->calculate_claude_cost($model, $tokens_used);

wp_send_json_success(array(
‘titles’ => $parsed_content[‘titles’],
‘hashtags’ => $parsed_content[‘hashtags’],
‘article’ => $parsed_content[‘article’],
‘cost’ => number_format($cost, 4),
‘tokens_used’ => number_format($tokens_used),
‘model_used’ => $model
));
}

/**
* Build Claude Prompt for Movie Article Generation
* Last Update: June 09, 2025 – Optimized prompt structure
* Purpose: Creates effective prompts for Claude AI
*/
private function build_claude_prompt($movie_title, $research_data, $personal_note, $movie_data) {
$prompt = “You are a professional movie journalist writing an engaging article about ‘{$movie_title}’. “;

if (!empty($personal_note)) {
$prompt .= “Style guidance: {$personal_note}\n\n”;
}

$prompt .= “Based on the following research data, create a comprehensive movie article:\n\n”;
$prompt .= “RESEARCH DATA:\n{$research_data}\n\n”;

if (!empty($movie_data[‘plot’])) {
$prompt .= “MOVIE PLOT:\n{$movie_data[‘plot’]}\n\n”;
}

$prompt .= “Please provide your response in this exact format:\n\n”;
$prompt .= “TITLES:\n[Provide 5 engaging article titles, one per line]\n\n”;
$prompt .= “HASHTAGS:\n[Provide 10-15 relevant hashtags, separated by spaces]\n\n”;
$prompt .= “ARTICLE:\n[Write a comprehensive 800-1200 word article with proper paragraphs, engaging introduction, detailed analysis, and strong conclusion]\n\n”;

return $prompt;
}

/**
* Call Claude API
* Last Update: June 09, 2025 – Robust API integration
* Purpose: Makes authenticated calls to Claude API
*/
private function call_claude_api($api_key, $model, $prompt) {
$api_url = ‘https://api.anthropic.com/v1/messages’;

$body = array(
‘model’ => $model,
‘max_tokens’ => 4000,
‘messages’ => array(
array(
‘role’ => ‘user’,
‘content’ => $prompt
)
)
);

$args = array(
‘headers’ => array(
‘Content-Type’ => ‘application/json’,
‘x-api-key’ => $api_key,
‘anthropic-version’ => ‘2023-06-01’
),
‘body’ => json_encode($body),
‘timeout’ => 60
);

$response = wp_remote_post($api_url, $args);

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

$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);

if (isset($data[‘error’])) {
return new WP_Error(‘claude_api_error’, $data[‘error’][‘message’]);
}

return array(
‘content’ => $data[‘content’][0][‘text’],
‘usage’ => $data[‘usage’]
);
}

/**
* Parse Claude Response
* Last Update: June 09, 2025 – Enhanced parsing
* Purpose: Extracts titles, hashtags, and article from Claude response
*/
private function parse_claude_response($content) {
$parsed = array(
‘titles’ => ”,
‘hashtags’ => ”,
‘article’ => ”
);

// Extract titles
if (preg_match(‘/TITLES:\s*(.*?)\s*(?=HASHTAGS:|$)/s’, $content, $matches)) {
$parsed[‘titles’] = trim($matches[1]);
}

// Extract hashtags
if (preg_match(‘/HASHTAGS:\s*(.*?)\s*(?=ARTICLE:|$)/s’, $content, $matches)) {
$parsed[‘hashtags’] = trim($matches[1]);
}

// Extract article
if (preg_match(‘/ARTICLE:\s*(.*?)$/s’, $content, $matches)) {
$parsed[‘article’] = trim($matches[1]);
}

return $parsed;
}

/**
* Calculate Claude API Cost
* Last Update: June 09, 2025 – Current pricing
* Purpose: Calculates actual cost based on token usage
*/
private function calculate_claude_cost($model, $tokens_used) {
$pricing = array(
‘claude-3-5-sonnet-20241022’ => 3.00,
‘claude-3-haiku-20240307’ => 0.25,
‘claude-3-opus-20240229’ => 15.00,
‘claude-3-5-haiku-20241022’ => 1.00
);

$cost_per_1m = isset($pricing[$model]) ? $pricing[$model] : 3.00;
return ($tokens_used / 1000000) * $cost_per_1m;
}

// Claude AI Content Generation Handler – Ends Here

/**
* Process Streaming Data – Enhanced for Priority and Free Options
* Last Update: June 08, 2025 12:45 AM GMT – Added priority logic and free options separation
* Purpose: Separates priority platforms from free options for different display
* Features: Priority selection, free options filtering, persistent display data
*/
private function process_streaming_data($data) {
$platforms = array();
$free_platforms = array();
$priority_url = ”;

// ADD ERROR CHECK
if (!is_array($data)) {
error_log(‘CDM: Invalid data format received from API’);
return array(‘platforms’ => array(), ‘free_platforms’ => array(), ‘priority_url’ => ”, ‘enhanced_display’ => ”);
}

error_log(‘CDM: Processing streaming data – Raw response: ‘ . print_r($data, true));

// Check if streaming options exist
if (!isset($data[‘streamingOptions’]) || empty($data[‘streamingOptions’])) {
error_log(‘CDM: No streamingOptions found in API response’);
return array(‘platforms’ => array(), ‘free_platforms’ => array(), ‘priority_url’ => ”, ‘enhanced_display’ => ”);
}

// Priority order for main field (subscription services)
$priority_platforms = array(‘netflix’, ‘disney’, ‘hulu’, ‘amazon’, ‘hbo’, ‘apple’, ‘paramount’);

// Free platforms priority order
$free_priority = array(‘tubi’, ‘peacock’, ‘crackle’, ‘pluto’, ‘imdb’, ‘roku’, ‘vudu’);

// Handle different API response structures
foreach ($data[‘streamingOptions’] as $country_or_option => $options_or_data) {
error_log(‘CDM: Processing country/option: ‘ . $country_or_option);

// If this is a country-based response (like “us” => […], “uk” => […])
if (is_array($options_or_data) && !isset($options_or_data[‘service’])) {
foreach ($options_or_data as $option) {
$platform_data = $this->extract_platform_data($option, $priority_platforms);
if ($platform_data) {
// Separate free platforms from paid ones
if ($platform_data[‘type’] === ‘Free’) {
$platform_data[‘free_priority’] = array_search(strtolower($platform_data[‘name’]), $free_priority);
$free_platforms[] = $platform_data;
} else {
$platforms[] = $platform_data;
}

// Set priority URL (first high-priority subscription service)
if (empty($priority_url) && in_array(strtolower($platform_data[‘name’]), $priority_platforms)) {
$priority_url = $platform_data[‘url’];
}
}
}
}
// If this is a direct options array
else {
$platform_data = $this->extract_platform_data($options_or_data, $priority_platforms);
if ($platform_data) {
// Separate free platforms from paid ones
if ($platform_data[‘type’] === ‘Free’) {
$platform_data[‘free_priority’] = array_search(strtolower($platform_data[‘name’]), $free_priority);
$free_platforms[] = $platform_data;
} else {
$platforms[] = $platform_data;
}

// Set priority URL (first high-priority subscription service)
if (empty($priority_url) && in_array(strtolower($platform_data[‘name’]), $priority_platforms)) {
$priority_url = $platform_data[‘url’];
}
}
}
}

// If no priority URL found, use first available subscription service
if (empty($priority_url) && !empty($platforms)) {
$priority_url = $platforms[0][‘url’];
}

// Sort free platforms by priority and limit to 4
usort($free_platforms, function($a, $b) {
$a_priority = $a[‘free_priority’] !== false ? $a[‘free_priority’] : 999;
$b_priority = $b[‘free_priority’] !== false ? $b[‘free_priority’] : 999;
return $a_priority – $b_priority;
});

// Limit to 4 free options
$free_platforms = array_slice($free_platforms, 0, 4);

error_log(‘CDM: Priority URL: ‘ . $priority_url);
error_log(‘CDM: Free platforms found: ‘ . count($free_platforms));

// Generate persistent display for free options
$free_display = $this->generate_free_options_display($free_platforms);

return array(
‘platforms’ => $platforms,
‘free_platforms’ => $free_platforms,
‘priority_url’ => $priority_url,
‘free_display’ => $free_display
);
}

/**
* Extract Platform Data – Helper Function
* Purpose: Safely extracts platform data from API response
*/
private function extract_platform_data($option, $priority_platforms) {
if (!is_array($option)) {
error_log(‘CDM: Invalid option data type: ‘ . gettype($option));
return false;
}

// Extract service name safely
$service_name = ”;
if (isset($option[‘service’][‘name’])) {
$service_name = $option[‘service’][‘name’];
} elseif (isset($option[‘service’]) && is_string($option[‘service’])) {
$service_name = $option[‘service’];
} else {
error_log(‘CDM: No service name found in option: ‘ . print_r($option, true));
$service_name = ‘Unknown Service’;
}

// Extract URL safely
$service_url = ”;
if (isset($option[‘link’])) {
$service_url = $option[‘link’];
} elseif (isset($option[‘url’])) {
$service_url = $option[‘url’];
}

// Skip if no valid URL
if (empty($service_url)) {
error_log(‘CDM: No valid URL found for service: ‘ . $service_name);
return false;
}

// Validate URL format
if (!filter_var($service_url, FILTER_VALIDATE_URL)) {
error_log(‘CDM: Invalid URL format: ‘ . $service_url);
return false;
}

// Extract type safely
$service_type = ‘subscription’;
if (isset($option[‘type’])) {
$service_type = $option[‘type’];
}

return array(
‘name’ => $service_name,
‘url’ => $service_url,
‘type’ => $this->get_streaming_type($service_type),
‘quality’ => isset($option[‘quality’]) ? $option[‘quality’] : ‘HD’,
‘price’ => isset($option[‘price’]) ? $option[‘price’] : null,
‘logo’ => isset($option[‘service’][‘imageSet’][‘lightThemeImage’]) ? $option[‘service’][‘imageSet’][‘lightThemeImage’] : ”,
‘priority’ => array_search(strtolower($service_name), $priority_platforms)
);
}

/**
* Get Streaming Type
* Purpose: Converts API streaming types to user-friendly labels
*/
private function get_streaming_type($api_type) {
$type_mapping = array(
‘subscription’ => ‘Subscription’,
‘rent’ => ‘Rental’,
‘buy’ => ‘Purchase’,
‘free’ => ‘Free’,
‘addon’ => ‘Add-on’
);

return isset($type_mapping[$api_type]) ? $type_mapping[$api_type] : ‘Available’;
}

/**
* Generate Free Options Display
* Purpose: Creates persistent HTML for free streaming options
* Features: Up to 4 free options, persistent after page refresh
*/
private function generate_free_options_display($free_platforms) {
if (empty($free_platforms)) {
return ”;
}

$html = ‘<div class=”cdm-free-options-display”>’;
$html .= ‘<h4 class=”free-options-title”>🆓 Free Streaming Options</h4>’;
$html .= ‘<div class=”free-options-grid”>’;

foreach ($free_platforms as $platform) {
$html .= ‘<div class=”free-option-item”>’;
$html .= ‘<div class=”free-platform-info”>’;
$html .= ‘<strong>’ . esc_html($platform[‘name’]) . ‘</strong>’;
$html .= ‘</div>’;
$html .= ‘<div class=”free-platform-details”>’;
$html .= ‘<a href=”‘ . esc_url($platform[‘url’]) . ‘” target=”_blank”>Watch Free</a>’;
$html .= ‘<br>’ . esc_html($platform[‘quality’]);
if (!empty($platform[‘price’])) {
$html .= ‘ • ‘ . esc_html($platform[‘price’]);
}
$html .= ‘</div>’;
$html .= ‘</div>’;
}

$html .= ‘</div>’;
$html .= ‘</div>’;

return $html;
}
}
// End of CDM_Draft_Manager class

### class-draft-form.php
✅ 5-step workflow rendering complete
✅ Tab 3 Claude AI interface implemented

<?php
/**
* CDM Draft Form Class – YouTube Transcript Integration Fixed
* Last Update: June 09, 2025 at 04:00 AM GMT – Fixed AJAX issues and form submission
* Purpose: Maintains original 5-step workflow with working YouTube transcript fetching
* Features: Original design + functional YouTube transcript fetching directly into Research Data
*/

class CDM_Draft_Form {
private $post_id;
private $draft_manager;

public function __construct($post_id, $draft_manager) {
$this->post_id = $post_id;
$this->draft_manager = $draft_manager;
}

public function render() {
$post_id = $this->post_id;
$current_step = isset($_GET[‘step’]) ? intval($_GET[‘step’]) : 1;

// Get saved movie data
$saved_movie_data = CDM_IMDB_Fetcher::get_saved_movie_data($post_id);
$has_movie_data = !empty($saved_movie_data[‘title’]);

// Get saved form data (REMOVED sources – replaced with YouTube transcript fetcher)
$imdb_url = get_post_meta($post_id, ‘cdm_imdb_url’, true);
$youtube_embed = get_post_meta($post_id, ‘cdm_youtube_embed’, true);
$streaming_platform = get_post_meta($post_id, ‘cdm_streaming_platform’, true);
$free_streaming_display = get_post_meta($post_id, ‘cdm_free_streaming_display’, true);
$research_data = get_post_meta($post_id, ‘cdm_research_data’, true);
$prompt = get_post_meta($post_id, ‘cdm_prompt’, true);
$generated_titles = get_post_meta($post_id, ‘cdm_generated_titles’, true);
$hashtags = get_post_meta($post_id, ‘cdm_hashtags’, true);
$ai_article = get_post_meta($post_id, ‘cdm_ai_article’, true);
$translation = get_post_meta($post_id, ‘cdm_translation’, true);
$final_edit = get_post_meta($post_id, ‘cdm_final_edit’, true);
?>
<div class=”cdm-form-container”>
<!– Step Navigation –>
<div class=”cdm-step-nav”>
<div class=”cdm-step <?php echo $current_step == 1 ? ‘active’ : ”; ?>” data-step=”1″>
<span class=”step-number”>1</span>
<span class=”step-label”>Movie Info</span>
</div>
<div class=”cdm-step <?php echo $current_step == 2 ? ‘active’ : ”; ?>” data-step=”2″>
<span class=”step-number”>2</span>
<span class=”step-label”>Research Data</span>
</div>
<div class=”cdm-step <?php echo $current_step == 3 ? ‘active’ : ”; ?>” data-step=”3″>
<span class=”step-number”>3</span>
<span class=”step-label”>AI Generation</span>
</div>
<div class=”cdm-step <?php echo $current_step == 4 ? ‘active’ : ”; ?>” data-step=”4″>
<span class=”step-number”>4</span>
<span class=”step-label”>Content Creation</span>
</div>
<div class=”cdm-step <?php echo $current_step == 5 ? ‘active’ : ”; ?>” data-step=”5″>
<span class=”step-number”>5</span>
<span class=”step-label”>Final Edit</span>
</div>
</div>

<!– Auto-save Status –>
<div id=”cdm-autosave-status” class=”cdm-autosave-status”>
<span class=”status-text”>Ready</span>
</div>

<!– Step 1: Movie/TV Information –>
<div id=”cdm-step-1″ class=”cdm-form-step <?php echo $current_step == 1 ? ‘active’ : ”; ?>”>
<div class=”cdm-section-header”>
<h2>Movie/TV Information</h2>
</div>

<div class=”cdm-form-row”>
<label for=”cdm_imdb_url”>IMDB URL</label>
<div class=”cdm-input-group”>
<input type=”url” id=”cdm_imdb_url” name=”cdm_imdb_url”
value=”<?php echo esc_attr($imdb_url); ?>”
placeholder=”https://www.imdb.com/title/tt…”
class=”cdm-auto-save” data-section=”imdb_url”>
<button type=”button” id=”cdm-fetch-imdb” class=”button button-primary” data-post-id=”<?php echo $post_id; ?>”>
🔍 Fetch Data
</button>
</div>
</div>

<!– Enhanced Movie Information Display –>
<div id=”cdm-imdb-data-display”>
<?php if ($has_movie_data): ?>
<div class=”cdm-imdb-data-enhanced”>
<h4 class=”movie-info-header”>Enhanced Movie Information Retrieved</h4>

<!– Main Movie Info Grid: Poster, Title, Cast, etc. –>
<div class=”cdm-imdb-main-grid”>
<!– Poster Column with Minimalistic Stats –>
<?php if (!empty($saved_movie_data[‘poster’]) || !empty($saved_movie_data[‘local_poster’])): ?>
<div class=”cdm-imdb-poster-column”>
<?php
$poster_url = !empty($saved_movie_data[‘local_poster’]) ? $saved_movie_data[‘local_poster’] : $saved_movie_data[‘poster’];
if ($poster_url): ?>
<img src=”<?php echo esc_url($poster_url); ?>” alt=”Movie Poster” class=”movie-poster”>
<?php endif; ?>

<!– Clean White Stats Buttons –>
<div class=”cdm-quick-stats”>
<?php if (!empty($saved_movie_data[‘rating’])): ?>
<div class=”stat-item stat-minimalistic”>
<span class=”stat-icon”>⭐</span>
<span class=”stat-value”><?php echo esc_html($saved_movie_data[‘rating’]); ?>/10</span>
</div>
<?php endif; ?>

<?php if (!empty($saved_movie_data[‘year’])): ?>
<div class=”stat-item stat-minimalistic”>
<span class=”stat-icon”>📅</span>
<span class=”stat-value”><?php echo esc_html($saved_movie_data[‘year’]); ?></span>
</div>
<?php endif; ?>

<?php if (!empty($saved_movie_data[‘duration’])): ?>
<div class=”stat-item stat-minimalistic”>
<span class=”stat-icon”>⏱️</span>
<span class=”stat-value”><?php echo esc_html($saved_movie_data[‘duration’]); ?></span>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>

<!– Main Details Column –>
<div class=”cdm-imdb-details-column”>
<!– Title and Basic Info –>
<?php if (!empty($saved_movie_data[‘title’])): ?>
<h3 class=”movie-title”>
<?php echo esc_html($saved_movie_data[‘title’]); ?>
<?php if (!empty($saved_movie_data[‘year’])): ?>
<span class=”movie-year”>(<?php echo esc_html($saved_movie_data[‘year’]); ?>)</span>
<?php endif; ?>
</h3>
<?php endif; ?>

<!– Crew Information –>
<div class=”cdm-crew-section”>
<?php if (!empty($saved_movie_data[‘director’])): ?>
<div class=”crew-item”>
<span class=”crew-label”>Director:</span>
<span class=”crew-value”><?php echo esc_html($saved_movie_data[‘director’]); ?></span>
</div>
<?php endif; ?>

<?php if (!empty($saved_movie_data[‘writer’])): ?>
<div class=”crew-item”>
<span class=”crew-label”>Writer:</span>
<span class=”crew-value”><?php echo esc_html($saved_movie_data[‘writer’]); ?></span>
</div>
<?php endif; ?>

<?php if (!empty($saved_movie_data[‘country’])): ?>
<div class=”crew-item”>
<span class=”crew-label”>Country:</span>
<span class=”crew-value”><?php echo esc_html($saved_movie_data[‘country’]); ?></span>
</div>
<?php endif; ?>

<?php if (!empty($saved_movie_data[‘production’])): ?>
<div class=”crew-item”>
<span class=”crew-label”>Production:</span>
<span class=”crew-value”><?php echo esc_html($saved_movie_data[‘production’]); ?></span>
</div>
<?php endif; ?>
</div>

<!– Genres –>
<?php if (!empty($saved_movie_data[‘genres’]) && is_array($saved_movie_data[‘genres’])): ?>
<div class=”cdm-genres-section”>
<span class=”section-label”>Genres:</span>
<div class=”genre-tags”>
<?php foreach ($saved_movie_data[‘genres’] as $genre): ?>
<span class=”genre-tag”><?php echo esc_html($genre); ?></span>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>

<!– Plot –>
<?php if (!empty($saved_movie_data[‘plot’])): ?>
<div class=”cdm-plot-section”>
<span class=”section-label”>Plot:</span>
<p class=”plot-text”><?php echo esc_html($saved_movie_data[‘plot’]); ?></p>
</div>
<?php endif; ?>
</div>
</div>

<!– Cast Section –>
<?php if (!empty($saved_movie_data[‘top_cast’]) && is_array($saved_movie_data[‘top_cast’])): ?>
<div class=”cdm-cast-section”>
<h4 class=”section-title”>Top Cast</h4>
<div class=”cast-grid”>
<?php foreach ($saved_movie_data[‘top_cast’] as $actor): ?>
<div class=”cast-member”>
<div class=”actor-name”><?php echo esc_html($actor[‘name’]); ?></div>
<?php if (!empty($actor[‘character’]) && $actor[‘character’] != ‘Unknown Role’): ?>
<div class=”character-name”>as <?php echo esc_html($actor[‘character’]); ?></div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>

<!– Box Office Financial Information –>
<?php if (!empty($saved_movie_data[‘budget’]) || !empty($saved_movie_data[‘box_office_worldwide’]) || !empty($saved_movie_data[‘box_office_usa’]) || !empty($saved_movie_data[‘opening_weekend’])): ?>
<div class=”cdm-section-divider”></div>
<div class=”cdm-financial-section”>
<h4 class=”section-title”>📊 Box Office & Financial Information</h4>
<div class=”financial-grid”>
<?php if (!empty($saved_movie_data[‘budget’])): ?>
<div class=”financial-item”>
<span class=”financial-label”>Budget:</span>
<span class=”financial-value”><?php echo esc_html($saved_movie_data[‘budget’]); ?></span>
</div>
<?php endif; ?>

<?php if (!empty($saved_movie_data[‘box_office_worldwide’])): ?>
<div class=”financial-item”>
<span class=”financial-label”>Worldwide Box Office:</span>
<span class=”financial-value”><?php echo esc_html($saved_movie_data[‘box_office_worldwide’]); ?></span>
</div>
<?php endif; ?>

<?php if (!empty($saved_movie_data[‘box_office_usa’])): ?>
<div class=”financial-item”>
<span class=”financial-label”>USA & Canada:</span>
<span class=”financial-value”><?php echo esc_html($saved_movie_data[‘box_office_usa’]); ?></span>
</div>
<?php endif; ?>

<?php if (!empty($saved_movie_data[‘opening_weekend’])): ?>
<div class=”financial-item”>
<span class=”financial-label”>Opening Weekend:</span>
<span class=”financial-value”><?php echo esc_html($saved_movie_data[‘opening_weekend’]); ?></span>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
</div>

<!– Streaming Platform Input Field –>
<div class=”cdm-form-row”>
<label for=”cdm_streaming_platform”>Main Streaming Platform</label>
<div class=”cdm-input-group”>
<input type=”text” id=”cdm_streaming_platform” name=”cdm_streaming_platform”
value=”<?php echo esc_attr($streaming_platform); ?>”
placeholder=”Netflix, Amazon Prime, Disney+, etc.”
class=”cdm-auto-save” data-section=”streaming_platform”>
<button type=”button” id=”cdm-fetch-streaming” class=”button button-secondary” data-post-id=”<?php echo $post_id; ?>”>
🔍 Fetch Platform
</button>
</div>
<p class=”input-description”>Primary streaming platform URL (auto-populated with most popular option)</p>
</div>

<!– Persistent Free Streaming Options Display –>
<?php if (!empty($free_streaming_display)): ?>
<div class=”cdm-persistent-free-options”>
<?php echo $free_streaming_display; ?>
</div>
<?php endif; ?>

<!– YouTube Trailer Section –>
<div class=”cdm-form-row”>
<label for=”cdm_youtube_embed”>YouTube Trailer Embed</label>
<div class=”cdm-input-group”>
<textarea id=”cdm_youtube_embed” name=”cdm_youtube_embed”
placeholder=”Paste YouTube embed code here…”
class=”cdm-auto-save” data-section=”youtube_embed”><?php echo esc_textarea($youtube_embed); ?></textarea>
<div class=”cdm-trailer-controls”>
<select id=”cdm-trailer-language” class=”cdm-language-select”>
<option value=”en”>English</option>
<option value=”es”>Spanish</option>
</select>
<button type=”button” id=”cdm-fetch-trailer” class=”button button-secondary” data-post-id=”<?php echo $post_id; ?>”>
🎬 Fetch Trailer
</button>
</div>
</div>
</div>

<!– YouTube Embed Preview –>
<div id=”cdm-embed-preview” class=”cdm-embed-preview” style=”<?php echo empty($youtube_embed) ? ‘display:none;’ : ”; ?>”>
<strong>Preview:</strong>
<div style=”margin-top: 15px; text-align: center;”>
<div class=”cdm-youtube-container”>
<?php echo $youtube_embed; ?>
</div>
</div>
</div>

<!– Reviews Section –>
<?php if (!empty($saved_movie_data[‘reviews’]) && is_array($saved_movie_data[‘reviews’])): ?>
<div class=”cdm-section-divider”></div>
<div class=”cdm-reviews-section”>
<!– Section Header with Icon –>
<h4 class=”section-title”>⭐ Top User Reviews</h4>

<!– Reviews Grid Container –>
<div class=”reviews-grid”>
<?php
// Loop through first 5 reviews only for display performance
foreach (array_slice($saved_movie_data[‘reviews’], 0, 5) as $index => $review): ?>
<!– Individual Review Card –>
<div class=”review-card”>
<!– Review Header: Title and Rating –>
<div class=”review-header”>
<span class=”review-title”><?php echo esc_html($review[‘title’]); ?></span>
<?php if (!empty($review[‘rating’])): ?>
<span class=”review-rating”><?php echo esc_html($review[‘rating’]); ?></span>
<?php endif; ?>
</div>

<!– Review Meta: Author and Date –>
<div class=”review-meta”>
<span class=”review-author”>by <?php echo esc_html($review[‘author’]); ?></span>
<?php if (!empty($review[‘date’])): ?>
<span class=”review-date”><?php echo esc_html($review[‘date’]); ?></span>
<?php endif; ?>
</div>

<!– Review Content with Expandable Text –>
<div class=”review-content”>
<div class=”review-text” id=”review-text-<?php echo $index; ?>”>
<?php echo esc_html($review[‘text’]); ?>

<!– Read More/Less Functionality –>
<?php if (!empty($review[‘is_truncated’]) && $review[‘is_truncated’]): ?>
<span class=”cdm-read-more-dots”>…</span>
<a href=”#” class=”cdm-read-more-link” data-index=”<?php echo $index; ?>”>Read More</a>

<!– Hidden Full Text Container –>
<div class=”cdm-full-text” id=”full-text-<?php echo $index; ?>” style=”display: none;”>
<?php echo esc_html($review[‘full_text’]); ?>
<a href=”#” class=”cdm-read-less-link” data-index=”<?php echo $index; ?>”>Read Less</a>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>

<!– Step 2: Research Data –>
<div id=”cdm-step-2″ class=”cdm-form-step <?php echo $current_step == 2 ? ‘active’ : ”; ?>”>
<div class=”cdm-section-header”>
<h2>Research Data</h2>
</div>

<!– YouTube Transcript Fetcher Section – Exactly like standalone plugin –>
<div class=”yt-transcript-wrap”>
<div class=”yt-transcript-container”>
<div class=”yt-card”>
<div id=”transcript-form”>
<table class=”form-table” role=”presentation”>
<tbody>
<tr>
<th scope=”row”>
<label for=”youtube-urls”>
YouTube URLs
</label>
</th>
<td>
<textarea
id=”youtube-urls”
rows=”6″
cols=”50″
placeholder=”https://www.youtube.com/watch?v=VIDEO_ID&#10;https://www.youtube.com/watch?v=ANOTHER_ID&#10;…”
></textarea>
<p class=”description”>Enter up to 5 YouTube URLs, one per line</p>
</td>
</tr>
<tr>
<th scope=”row”>
<label for=”language-preference”>
Language Preference
</label>
</th>
<td>
<div class=”language-controls”>
<select id=”language-preference” name=”language-preference”>
<option value=”auto”>Auto (English → Spanish → Any)</option>
<option value=”en”>English Only</option>
<option value=”es”>Spanish Only</option>
<option value=”en-translate”>English (with translation)</option>
<option value=”es-translate”>Spanish (with translation)</option>
</select>
<button type=”button” id=”cdm-fetch-transcripts” class=”button button-primary”>
Fetch Transcripts
</button>
</div>
<p class=”description”>Choose your preferred transcript language</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>

<div id=”loading” class=”yt-card loading-card” style=”display:none;”>
<div class=”loading-content”>
<div class=”loading-spinner”></div>
<h3>Fetching transcripts…</h3>
<p>This may take a few moments depending on video length and availability</p>
</div>
</div>
</div>
</div>

<!– Research Data Field – Where transcripts are automatically saved –>
<div class=”cdm-form-row”>
<label for=”cdm_research_data”>Research Data</label>
<textarea id=”cdm_research_data” name=”cdm_research_data”
placeholder=”Your research data and YouTube transcripts will appear here…”
class=”cdm-auto-save” data-section=”research_data”
rows=”20″><?php echo esc_textarea($research_data); ?></textarea>
<p class=”input-description”>Research notes and fetched YouTube transcripts are automatically saved here</p>
</div>
</div>

<!– Step 3: AI Generation –>
<div id=”cdm-step-3″ class=”cdm-form-step <?php echo $current_step == 3 ? ‘active’ : ”; ?>”>
<div class=”cdm-section-header”>
<h2>🤖 Claude AI Generation</h2>
<p class=”section-description”>Generate high-quality movie articles using Claude AI</p>
</div>

<!– Claude Model Selection –>
<div class=”cdm-form-row”>
<label for=”cdm_claude_model”>Claude Model</label>
<div class=”cdm-model-selection”>
<select id=”cdm_claude_model” name=”cdm_claude_model” class=”cdm-auto-save” data-section=”claude_model”>
<option value=”claude-3-5-sonnet-20241022″>Claude 3.5 Sonnet (Best Quality) – $3/1M tokens</option>
<option value=”claude-3-haiku-20240307″>Claude 3 Haiku (Fast & Cheap) – $0.25/1M tokens</option>
<option value=”claude-3-opus-20240229″>Claude 3 Opus (Most Powerful) – $15/1M tokens</option>
<option value=”claude-3-5-haiku-20241022″>Claude 3.5 Haiku (Balanced) – $1/1M tokens</option>
</select>
<div class=”model-info”>
<span id=”estimated-cost”>Estimated cost: $0.00</span>
<span class=”cost-calculator”>📊</span>
</div>
</div>
<p class=”input-description”>Choose your Claude model based on quality needs and budget</p>
</div>

<!– Personal Note for Tone –>
<div class=”cdm-form-row”>
<label for=”cdm_personal_note”>Personal Note (Tone & Style)</label>
<textarea id=”cdm_personal_note” name=”cdm_personal_note”
placeholder=”Add specific instructions for tone, style, or focus areas…”
class=”cdm-auto-save” data-section=”personal_note” rows=”3″><?php echo esc_textarea(get_post_meta($post_id, ‘cdm_personal_note’, true)); ?></textarea>
<p class=”input-description”>Optional: Guide the AI’s writing style and tone</p>
</div>

<!– AI Generation Button –>
<div class=”cdm-form-row”>
<div class=”cdm-ai-generation-controls”>
<button type=”button” id=”cdm-generate-content” class=”button button-primary button-large” data-post-id=”<?php echo $post_id; ?>”>
🚀 Generate Article with Claude AI
</button>
<div class=”generation-status”>
<span id=”generation-progress” style=”display:none;”>
<span class=”dashicons dashicons-update spin”></span>
Generating content…
</span>
</div>
</div>
</div>

<!– Generated Content Fields –>
<div class=”cdm-form-row”>
<label for=”cdm_generated_draft”>Generated Article Draft</label>
<textarea id=”cdm_generated_draft” name=”cdm_generated_draft”
placeholder=”AI-generated article content will appear here…”
class=”cdm-auto-save” data-section=”generated_draft” rows=”15″><?php echo esc_textarea(get_post_meta($post_id, ‘cdm_generated_draft’, true)); ?></textarea>
<p class=”input-description”>Complete article draft generated by Claude AI</p>
</div>
</div>

<!– Step 4: Content Creation –>
<div id=”cdm-step-4″ class=”cdm-form-step <?php echo $current_step == 4 ? ‘active’ : ”; ?>”>
<div class=”cdm-section-header”>
<h2>Content Creation</h2>
</div>

<div class=”cdm-form-row”>
<label for=”cdm_ai_article”>AI Generated Article</label>
<textarea id=”cdm_ai_article” name=”cdm_ai_article”
placeholder=”AI generated article will appear here…”
class=”cdm-auto-save” data-section=”ai_article”><?php echo esc_textarea($ai_article); ?></textarea>
</div>

<div class=”cdm-form-row”>
<label for=”cdm_translation”>Translation</label>
<textarea id=”cdm_translation” name=”cdm_translation”
placeholder=”Translation will appear here…”
class=”cdm-auto-save” data-section=”translation”><?php echo esc_textarea($translation); ?></textarea>
</div>
</div>

<!– Step 5: Final Edit –>
<div id=”cdm-step-5″ class=”cdm-form-step <?php echo $current_step == 5 ? ‘active’ : ”; ?>”>
<div class=”cdm-section-header”>
<h2>Final Edit</h2>
</div>

<div class=”cdm-form-row”>
<label for=”cdm_final_edit”>Final Edit</label>
<textarea id=”cdm_final_edit” name=”cdm_final_edit”
placeholder=”Your final edited content…”
class=”cdm-auto-save” data-section=”final_edit”><?php echo esc_textarea($final_edit); ?></textarea>
</div>

<div class=”cdm-actions”>
<button type=”button” id=”cdm-check-plagiarism” class=”button button-secondary”>
<span class=”dashicons dashicons-search”></span> Check Plagiarism
</button>
<button type=”button” id=”cdm-ai-detect” class=”button button-secondary”>
<span class=”dashicons dashicons-admin-tools”></span> AI Detection
</button>
</div>

<div id=”cdm-plagiarism-results”></div>
<div id=”cdm-ai-results”></div>
</div>

<!– Hidden Fields for Post ID Detection –>
<input type=”hidden” id=”cdm-post-id” value=”<?php echo $post_id; ?>”>
<input type=”hidden” id=”cdm-current-post-id” value=”<?php echo $post_id; ?>”>
<input type=”hidden” name=”cdm_post_id_backup” value=”<?php echo $post_id; ?>”>

<!– WordPress Localized Script Data –>
<script type=”text/javascript”>
var cdm_post_data = {
post_id: <?php echo $post_id; ?>,
ajaxurl: ‘<?php echo admin_url(‘admin-ajax.php’); ?>’,
nonce: ‘<?php echo wp_create_nonce(‘cdm_nonce’); ?>’
};
</script>
</div>

<?php
$this->render_styles();
$this->render_scripts();
}

private function render_styles() {
?>
<style>
/* Original CDM Styles – Restored */
.cdm-form-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

/* Step Navigation */
.cdm-step-nav {
display: flex;
justify-content: space-between;
margin-bottom: 30px;
background: #f8f9fa;
border-radius: 8px;
padding: 10px;
}

.cdm-step {
flex: 1;
text-align: center;
padding: 15px 10px;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
margin: 0 5px;
}

.cdm-step:hover {
background: #e9ecef;
}

.cdm-step.active {
background: #007cba;
color: white;
}

.step-number {
display: block;
font-size: 18px;
font-weight: bold;
margin-bottom: 5px;
}

.step-label {
font-size: 12px;
text-transform: uppercase;
}

/* Form Steps */
.cdm-form-step {
display: none;
}

.cdm-form-step.active {
display: block;
}

.cdm-section-header h2 {
color: #23282d;
border-bottom: 2px solid #007cba;
padding-bottom: 10px;
margin-bottom: 25px;
}

/* Form Elements */
.cdm-form-row {
margin-bottom: 20px;
}

.cdm-form-row label {
display: block;
font-weight: 600;
margin-bottom: 8px;
color: #23282d;
}

.cdm-input-group {
display: flex;
gap: 10px;
align-items: flex-start;
}

.cdm-input-group input,
.cdm-input-group textarea {
flex: 1;
}

input[type=”text”],
input[type=”url”],
textarea,
select {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}

input:focus,
textarea:focus,
select:focus {
border-color: #007cba;
box-shadow: 0 0 0 1px #007cba;
outline: none;
}

.input-description {
font-size: 12px;
color: #666;
margin-top: 5px;
font-style: italic;
}

/* Buttons */
.button {
padding: 8px 16px;
border: 1px solid #007cba;
border-radius: 4px;
background: #007cba;
color: white;
cursor: pointer;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 14px;
}

.button:hover {
background: #005a87;
border-color: #005a87;
}

.button-secondary {
background: #6c757d;
border-color: #6c757d;
}

.button-secondary:hover {
background: #5a6268;
border-color: #5a6268;
}

.button:disabled {
opacity: 0.5;
cursor: not-allowed;
}

/* YouTube Transcript Fetcher Styles – Exactly like standalone plugin */
.yt-transcript-wrap {
max-width: 1200px;
margin-bottom: 30px;
}

.yt-transcript-container {
margin-top: 20px;
}

.yt-card {
background: #fff;
border: 1px solid #c3c4c7;
box-shadow: 0 1px 1px rgba(0,0,0,.04);
margin-bottom: 20px;
}

.yt-card .form-table {
margin: 0;
}

.yt-card .form-table th {
width: 200px;
padding: 20px 10px 20px 20px;
vertical-align: top;
text-align: left;
}

.yt-card .form-table td {
padding: 15px 20px 20px 10px;
text-align: left;
}

.yt-card .form-table th label {
font-weight: 600;
color: #23282d;
text-align: left;
}

#youtube-urls {
width: 100%;
max-width: 500px;
min-height: 120px;
font-family: Consolas, Monaco, monospace;
font-size: 13px;
}

.language-controls {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}

#language-preference {
width: 180px;
height: 32px;
}

.language-controls .button {
height: 32px;
line-height: 30px;
padding: 0 12px;
display: flex;
align-items: center;
gap: 5px;
white-space: nowrap;
width: 120px;
justify-content: center;
}

.loading-card {
text-align: center;
padding: 40px 20px;
background: #f6f7f7;
border-left: 4px solid #72aee6;
}

.loading-content h3 {
color: #1d2327;
margin: 15px 0 10px 0;
}

.loading-content p {
color: #646970;
margin: 0;
}

.loading-spinner {
border: 3px solid #f3f4f5;
border-top: 3px solid #2271b1;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin: 0 auto 20px auto;
}

@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

/* Movie Info Display */
.cdm-imdb-data-enhanced {
background: #f8f9fa;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
}

.movie-info-header {
color: #007cba;
margin-bottom: 20px;
font-size: 18px;
}

.cdm-imdb-main-grid {
display: grid;
grid-template-columns: 300px 1fr;
gap: 20px;
margin-bottom: 25px;
}

.cdm-imdb-poster-column {
display: flex;
flex-direction: column;
align-items: center;
}

.movie-poster {
width: 100%;
max-width: 300px;
height: auto;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
margin-bottom: 15px;
}

.cdm-quick-stats {
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
}

.stat-item.stat-minimalistic {
display: flex;
align-items: center;
justify-content: center;
padding: 8px 12px;
background: white;
border-radius: 6px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
font-size: 14px;
font-weight: 500;
}

.stat-icon {
margin-right: 8px;
font-size: 16px;
}

.stat-value {
color: #2c3e50;
}

.cdm-imdb-details-column {
display: flex;
flex-direction: column;
gap: 15px;
}

.movie-title {
font-size: 24px;
font-weight: bold;
color: #2c3e50;
margin: 0;
}

.movie-year {
color: #7f8c8d;
font-weight: normal;
}

.cdm-crew-section {
display: flex;
flex-direction: column;
gap: 8px;
}

.crew-item {
display: flex;
align-items: center;
gap: 10px;
}

.crew-label {
font-weight: 600;
min-width: 100px;
}

.crew-value {
color: #34495e;
}

.cdm-genres-section {
display: flex;
flex-direction: column;
gap: 10px;
}

.section-label {
font-weight: 600;
color: #2c3e50;
}

.genre-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}

.genre-tag {
background: #3498db;
color: white;
padding: 4px 12px;
border-radius: 15px;
font-size: 12px;
font-weight: 500;
}

.cdm-plot-section {
display: flex;
flex-direction: column;
gap: 10px;
}

.plot-text {
line-height: 1.6;
color: #34495e;
margin: 0;
}

/* Cast Section */
.cdm-cast-section {
margin-bottom: 25px;
padding: 20px;
background: rgba(255, 255, 255, 0.7);
border-radius: 8px;
}

.section-title {
font-size: 18px;
font-weight: 600;
color: #2c3e50;
margin-bottom: 15px;
}

.cast-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
}

.cast-member {
padding: 12px;
background: white;
border-radius: 6px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
text-align: center;
}

.actor-name {
font-weight: 600;
color: #2c3e50;
margin-bottom: 5px;
}

.character-name {
font-size: 12px;
color: #7f8c8d;
font-style: italic;
}

/* Financial Section */
.cdm-financial-section {
margin-bottom: 25px;
padding: 20px;
background: rgba(255, 255, 255, 0.7);
border-radius: 8px;
}

.financial-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
}

.financial-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
background: white;
border-radius: 6px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.financial-label {
font-weight: 600;
color: #2c3e50;
}

.financial-value {
font-weight: 500;
color: #27ae60;
}

/* Reviews Section */
.cdm-reviews-section {
margin-bottom: 25px;
padding: 20px;
background: rgba(255, 255, 255, 0.7);
border-radius: 8px;
}

.reviews-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 15px;
}

.review-card {
padding: 15px;
background: white;
border-radius: 6px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
border-left: 4px solid #ffc107;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.review-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}

.review-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}

.review-title {
font-weight: 600;
color: #2c3e50;
flex: 1;
}

.review-rating {
background: #28a745;
color: white;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
flex-shrink: 0;
}

.review-meta {
margin-bottom: 10px;
font-size: 12px;
color: #6c757d;
display: flex;
gap: 15px;
}

.review-content {
line-height: 1.6;
color: #444;
}

.cdm-read-more-link,
.cdm-read-less-link {
color: #0073aa;
text-decoration: none;
font-weight: bold;
cursor: pointer;
transition: color 0.2s ease;
}

.cdm-read-more-link:hover,
.cdm-read-less-link:hover {
color: #005177;
text-decoration: underline;
}

.cdm-full-text {
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid #eee;
}

/* Navigation */
.cdm-navigation {
display: flex;
justify-content: space-between;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #ddd;
}

/* Auto-save Status */
.cdm-autosave-status {
text-align: center;
margin-bottom: 20px;
padding: 10px;
background: #f8f9fa;
border-radius: 4px;
}

.status-text {
font-size: 14px;
color: #666;
}

/* Responsive Design */
@media (max-width: 768px) {
.cdm-imdb-main-grid {
grid-template-columns: 1fr;
gap: 15px;
}

.cdm-step-nav {
flex-direction: column;
gap: 10px;
}

.cdm-input-group {
flex-direction: column;
}

.cast-grid {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}

.financial-grid {
grid-template-columns: 1fr;
}

.reviews-grid {
grid-template-columns: 1fr;
}

.yt-card .form-table th,
.yt-card .form-table td {
display: block;
width: 100%;
padding: 10px 20px;
text-align: left;
}

.yt-card .form-table th {
padding-bottom: 5px;
}

.language-controls {
flex-direction: column;
align-items: stretch;
}

.language-controls .button {
justify-content: center;
width: 100%;
}

#language-preference {
width: 100%;
}
}
</style>
<?php
}

private function render_scripts() {
?>
<script type=”text/javascript”>
jQuery(document).ready(function($) {
console.log(‘CDM Original Scripts Loading…’);

// Step Navigation
$(‘.cdm-step’).on(‘click’, function() {
var step = $(this).data(‘step’);

// Update active step
$(‘.cdm-step’).removeClass(‘active’);
$(this).addClass(‘active’);

// Show corresponding form step
$(‘.cdm-form-step’).removeClass(‘active’);
$(‘#cdm-step-‘ + step).addClass(‘active’);

// Update URL
var url = new URL(window.location);
url.searchParams.set(‘step’, step);
window.history.pushState({}, ”, url);
});

// YouTube Transcript Button Handler – FIXED VERSION
$(‘#cdm-fetch-transcripts’).on(‘click’, function(e) {
e.preventDefault();
e.stopPropagation();

console.log(‘=== TRANSCRIPT FETCH CLICKED ===’);

var urls = $(‘#youtube-urls’).val().trim();
var languagePreference = $(‘#language-preference’).val();
var postId = $(‘#cdm-post-id’).val() || cdm_post_data.post_id;

console.log(‘URLs:’, urls);
console.log(‘Language:’, languagePreference);
console.log(‘Post ID:’, postId);

if (!urls) {
alert(‘Please enter at least one YouTube URL’);
return false;
}

if (!postId) {
alert(‘Post ID not found. Please save the draft first.’);
return false;
}

// Validate YouTube URLs
var lines = urls.split(‘\n’);
var validUrls = [];

lines.forEach(function(line) {
line = line.trim();
if (line.includes(‘youtube.com/watch?v=’) || line.includes(‘youtu.be/’)) {
validUrls.push(line);
}
});

if (validUrls.length === 0) {
alert(‘No valid YouTube URLs found. Please check your URLs format.’);
return false;
}

if (validUrls.length > 5) {
alert(‘Maximum 5 YouTube URLs allowed. Please remove some URLs.’);
return false;
}

console.log(‘Valid URLs:’, validUrls);

$(‘#loading’).show();

$.ajax({
url: cdm_post_data.ajaxurl,
type: ‘POST’,
data: {
action: ‘cdm_fetch_transcripts’,
nonce: cdm_post_data.nonce,
post_id: postId, // FIXED: Changed from postid to post_id
urls: validUrls.join(‘\n’),
language_preference: languagePreference
},
success: function(response) {
console.log(‘=== TRANSCRIPT AJAX SUCCESS ===’);
console.log(‘Response:’, response);

$(‘#loading’).hide();

if (response.success) {
// Update the Research Data field with the new content
var currentResearchData = $(‘#cdm_research_data’).val();
var separator = currentResearchData ? ‘\n\n=== YOUTUBE TRANSCRIPTS ===\n\n’ : ”;
var updatedData = currentResearchData + separator + response.data.transcripts;

$(‘#cdm_research_data’).val(updatedData).trigger(‘input’);
alert(‘YouTube transcripts fetched successfully and added to Research Data!’);

// Clear the URLs field
$(‘#youtube-urls’).val(”);
} else {
alert(‘Failed to fetch transcripts: ‘ + (response.data || ‘Unknown error’));
}
},
error: function(xhr, status, error) {
console.log(‘=== TRANSCRIPT AJAX ERROR ===’);
console.log(‘XHR:’, xhr);
console.log(‘Status:’, status);
console.log(‘Error:’, error);
console.log(‘Response Text:’, xhr.responseText);

$(‘#loading’).hide();
alert(‘Network error while fetching transcripts: ‘ + error);
}
});

return false; // Prevent any form submission
});

// Fix Read More/Less functionality for reviews
$(document).on(‘click’, ‘.cdm-read-more-link’, function(e) {
e.preventDefault();
var index = $(this).data(‘index’);
var reviewText = $(‘#review-text-‘ + index);
var fullText = $(‘#full-text-‘ + index);
var dots = reviewText.find(‘.cdm-read-more-dots’);

dots.hide();
$(this).hide();
fullText.show();
});

$(document).on(‘click’, ‘.cdm-read-less-link’, function(e) {
e.preventDefault();
var index = $(this).data(‘index’);
var reviewText = $(‘#review-text-‘ + index);
var fullText = $(‘#full-text-‘ + index);
var dots = reviewText.find(‘.cdm-read-more-dots’);
var readMoreLink = reviewText.find(‘.cdm-read-more-link’);

fullText.hide();
dots.show();
readMoreLink.show();
});

console.log(‘CDM Original Scripts Loaded Successfully’);
});
</script>
<?php
}
}

### admin.js
✅ AJAX functionality operational
⚠️ Navigation event handlers need deduplication

/**
* CDM DRAFT FORM JAVASCRIPT FUNCTIONALITY
* Last Update: June 08, 2025 12:50 AM GMT – Fixed all variable naming issues and added streaming platform functionality
* Purpose: Handles all client-side interactions for the draft form
* Dependencies: jQuery, WordPress AJAX
* Features: Step navigation, IMDB fetching, YouTube trailer fetching, Auto-save, Embed preview, Streaming platform fetching
*/

jQuery(document).ready(function($) {

/**
* Auto-save functionality
*/
var autoSaveInterval = cdmajax.autosave_interval || 30000;
var autoSaveTimer;
var isAutoSaving = false;

// Initialize auto-save for draft forms
if ($(‘.cdm-auto-save’).length > 0) {
initAutoSave();
}

function initAutoSave() {
$(‘.cdm-auto-save’).on(‘input change’, function() {
clearTimeout(autoSaveTimer);
autoSaveTimer = setTimeout(function() {
performAutoSave();
}, 2000); // Wait 2 seconds after user stops typing
});

// Periodic auto-save
setInterval(performAutoSave, autoSaveInterval);
}

function performAutoSave() {
if (isAutoSaving) return;

var postId = $(‘#cdm-post-id’).val();
if (!postId) return;

var hasChanges = false;
var saveData = {};

$(‘.cdm-auto-save’).each(function() {
var section = $(this).data(‘section’);
var content = $(this).val();
if (section && content !== $(this).data(‘original-value’)) {
saveData[section] = content;
hasChanges = true;
}
});

if (!hasChanges) return;

isAutoSaving = true;
updateAutoSaveStatus(‘saving’, ‘Saving…’);

$.ajax({
url: cdmajax.ajaxurl,
type: ‘POST’,
data: {
action: ‘cdm_auto_save’,
nonce: cdmajax.nonce,
post_id: postId,
sections: saveData
},
success: function(response) {
if (response.success) {
updateAutoSaveStatus(‘saved’, ‘All changes saved at ‘ + response.data.timestamp);
// Update original values
$(‘.cdm-auto-save’).each(function() {
$(this).data(‘original-value’, $(this).val());
});
} else {
updateAutoSaveStatus(‘error’, ‘Save failed: ‘ + response.data);
}
},
error: function() {
updateAutoSaveStatus(‘error’, ‘Save failed: Network error’);
},
complete: function() {
isAutoSaving = false;
}
});
}

function updateAutoSaveStatus(status, message) {
var $status = $(‘#cdm-autosave-status’);
$status.removeClass(‘saving saved error’).addClass(status);
$status.find(‘.status-text’).text(message);

if (status === ‘saved’) {
setTimeout(function() {
$status.text(‘Ready’).removeClass(‘saved’);
}, 2000);
}
}

/**
* IMDB DATA FETCHING SYSTEM – FIXED VARIABLE NAMES
* Purpose: Fetches movie data from IMDB with TMDB enhancement
* Features: Robust post ID detection, error handling, UI feedback
* Dependencies: WordPress AJAX, CDM_IMDB_Fetcher class
*/

// IMDB Fetch Button Handler – Enhanced with robust post ID detection and duplicate prevention
$(‘#cdm-fetch-imdb’).off(‘click’).on(‘click’, function(e) {
e.preventDefault();
e.stopImmediatePropagation();

// Prevent multiple rapid clicks
if ($(this).hasClass(‘processing’)) {
return false;
}

var imdbUrl = $(‘#cdm_imdb_url’).val().trim();
var postId = getPostIdRobust();

console.log(‘=== FETCH DATA DEBUG START ===’);
console.log(‘IMDB URL:’, imdbUrl);
console.log(‘Detected Post ID:’, postId);

// Validate IMDB URL
if (!imdbUrl) {
console.log(‘ERROR: No IMDB URL entered’);
alert(‘Please enter an IMDB URL first.’);
return false;
}

if (!imdbUrl.includes(‘imdb.com/title/’)) {
console.log(‘ERROR: Invalid IMDB URL format’);
alert(‘Please enter a valid IMDB movie URL.’);
return false;
}

// Validate Post ID
if (!postId || postId == ‘0’) {
console.log(‘ERROR: No valid Post ID found’);
alert(‘Post ID not found. Please save the draft first.’);
return false;
}

// Check AJAX configuration – FIXED VARIABLE NAMES
console.log(‘AJAX URL:’, cdmajax.ajaxurl);
console.log(‘Nonce:’, cdmajax.nonce);

// Mark as processing to prevent duplicate requests
$(this).addClass(‘processing’);

var button = $(this);
var originalText = button.html();
console.log(‘Button original text:’, originalText);

// Update button state
button.prop(‘disabled’, true).html(‘🔍 Fetching Enhanced Data…’);
console.log(‘Button state updated, starting AJAX request…’);

// Execute AJAX request – FIXED VARIABLE NAMES
$.ajax({
url: cdmajax.ajaxurl,
type: ‘POST’,
data: {
action: ‘cdm_fetch_imdb’,
nonce: cdmajax.nonce,
imdb_url: imdbUrl,
post_id: postId
},
success: function(response) {
console.log(‘=== AJAX SUCCESS ===’);
console.log(‘Full response:’, response);

if (response.success) {
console.log(‘Response data:’, response.data);
displayImdbData(response.data);

// Auto-fill YouTube embed if available
if (response.data.youtube_embed) {
$(‘#cdm_youtube_embed’).val(response.data.youtube_embed).trigger(‘input’);
updateEmbedPreview(response.data.youtube_embed);
}
console.log(‘IMDB data displayed successfully’);
} else {
console.log(‘ERROR: Server returned failure’);
console.log(‘Error message:’, response.data);
alert(‘Failed to fetch IMDB data: ‘ + (response.data || ‘Unknown error’));
}
},
error: function(xhr, status, error) {
console.log(‘=== AJAX ERROR ===’);
console.log(‘XHR object:’, xhr);
console.log(‘Status:’, status);
console.log(‘Error:’, error);
console.log(‘Response text:’, xhr.responseText);
console.log(‘Status code:’, xhr.status);

alert(‘Network error while fetching IMDB data: ‘ + error);
},
complete: function() {
console.log(‘=== AJAX COMPLETE ===’);
console.log(‘Resetting button state’);

// Reset button state
button.prop(‘disabled’, false).html(originalText);
button.removeClass(‘processing’);
console.log(‘=== FETCH DATA DEBUG END ===’);
}
});

return false;
});

/**
* Robust Post ID Detection Function
* Uses multiple fallback methods to ensure post ID is found
* Based on WordPress AJAX best practices and search results
* @returns {string|null} The detected post ID or null if not found
*/
function getPostIdRobust() {
var postId = null;

// Method 1: Localized script data (most reliable)
if (typeof cdmpostdata !== ‘undefined’ && cdmpostdata.postid) {
postId = cdmpostdata.postid;
if (postId && postId != ‘0’) return postId;
}

// Method 2: Button data attribute
postId = $(‘#cdm-fetch-imdb’).data(‘post-id’);
if (postId && postId != ‘0’) return postId;

// Method 3: Hidden field
postId = $(‘#cdm-post-id’).val();
if (postId && postId != ‘0’) return postId;

// Method 4: Alternative hidden field
postId = $(‘#cdm-current-post-id’).val();
if (postId && postId != ‘0’) return postId;

// Method 5: WordPress post ID field
postId = $(‘#post_ID’).val();
if (postId && postId != ‘0’) return postId;

// Method 6: URL parameter
var urlParams = new URLSearchParams(window.location.search);
postId = urlParams.get(‘post’);
if (postId && postId != ‘0’) return postId;

// Method 7: Form input
postId = $(‘input[name=”post_ID”]’).val();
if (postId && postId != ‘0’) return postId;

// Method 8: Backup field
postId = $(‘input[name=”cdm_post_id_backup”]’).val();
if (postId && postId != ‘0’) return postId;

console.log(‘All post ID detection methods failed’);
return null;
}

/**
* Display IMDB Data Function
* Renders comprehensive movie information in the UI
* @param {Object} data – Movie data object from IMDB/TMDB
*/
function displayImdbData(data) {
var html = ‘<div class=”cdm-imdb-data-enhanced”>’;
html += ‘<h4 class=”movie-info-header”>🎬 Enhanced Movie Information Retrieved</h4>’;

// Main Movie Info Grid
html += ‘<div class=”cdm-imdb-main-grid”>’;

// Poster Column with Minimalistic Stats
if (data.local_poster || data.poster) {
html += ‘<div class=”cdm-imdb-poster-column”>’;
var posterUrl = data.local_poster || data.poster;
html += ‘<img src=”‘ + posterUrl + ‘” alt=”Movie Poster” class=”movie-poster” />’;

html += ‘<div class=”cdm-quick-stats”>’;
if (data.rating) {
html += ‘<div class=”stat-item stat-minimalistic”><span class=”stat-icon”>⭐</span><span class=”stat-value”>’ + data.rating + ‘/10</span></div>’;
}
if (data.year) {
html += ‘<div class=”stat-item stat-minimalistic”><span class=”stat-icon”>📅</span><span class=”stat-value”>’ + data.year + ‘</span></div>’;
}
if (data.duration) {
html += ‘<div class=”stat-item stat-minimalistic”><span class=”stat-icon”>⏱️</span><span class=”stat-value”>’ + data.duration + ‘</span></div>’;
}
html += ‘</div></div>’;
}

// Main Details Column
html += ‘<div class=”cdm-imdb-details-column”>’;
if (data.title) {
html += ‘<h3 class=”movie-title”>’ + data.title;
if (data.year) {
html += ‘ <span class=”movie-year”>(‘ + data.year + ‘)</span>’;
}
html += ‘</h3>’;
}

// Crew Information
html += ‘<div class=”cdm-crew-section”>’;
if (data.director) {
html += ‘<div class=”crew-item”><span class=”crew-label”>🎬 Director:</span><span class=”crew-value”>’ + data.director + ‘</span></div>’;
}
if (data.writer) {
html += ‘<div class=”crew-item”><span class=”crew-label”>✍️ Writer:</span><span class=”crew-value”>’ + data.writer + ‘</span></div>’;
}
if (data.country) {
html += ‘<div class=”crew-item”><span class=”crew-label”>🌍 Country:</span><span class=”crew-value”>’ + data.country + ‘</span></div>’;
}
if (data.production) {
html += ‘<div class=”crew-item”><span class=”crew-label”>🏭 Production:</span><span class=”crew-value”>’ + data.production + ‘</span></div>’;
}
html += ‘</div>’;

// Genres
if (data.genres && data.genres.length > 0) {
html += ‘<div class=”cdm-genres-section”>’;
html += ‘<span class=”section-label”>🎭 Genres:</span>’;
html += ‘<div class=”genre-tags”>’;
for (var i = 0; i < data.genres.length; i++) {
html += ‘<span class=”genre-tag”>’ + data.genres[i] + ‘</span>’;
}
html += ‘</div></div>’;
}

// Plot
if (data.plot) {
html += ‘<div class=”cdm-plot-section”>’;
html += ‘<span class=”section-label”>📖 Plot:</span>’;
html += ‘<p class=”plot-text”>’ + data.plot + ‘</p>’;
html += ‘</div>’;
}

html += ‘</div>’; // End details column
html += ‘</div>’; // End main grid

// Cast Section
if (data.top_cast && data.top_cast.length > 0) {
html += ‘<div class=”cdm-cast-section”>’;
html += ‘<h4 class=”section-title”>🎭 Top Cast</h4>’;
html += ‘<div class=”cast-grid”>’;
for (var i = 0; i < data.top_cast.length; i++) {
html += ‘<div class=”cast-member”>’;
html += ‘<div class=”actor-name”>’ + data.top_cast[i].name + ‘</div>’;
if (data.top_cast[i].character && data.top_cast[i].character != ‘Unknown Role’) {
html += ‘<div class=”character-name”>as ‘ + data.top_cast[i].character + ‘</div>’;
}
html += ‘</div>’;
}
html += ‘</div></div>’;
}

// Financial Information
if (data.budget || data.box_office_worldwide || data.box_office_usa || data.opening_weekend) {
html += ‘<div class=”cdm-section-divider”></div>’;
html += ‘<div class=”cdm-financial-section”>’;
html += ‘<h4 class=”section-title”>💰 Box Office & Financial Information</h4>’;
html += ‘<div class=”financial-grid”>’;
if (data.budget) {
html += ‘<div class=”financial-item”><span class=”financial-label”>💵 Budget:</span><span class=”financial-value”>’ + data.budget + ‘</span></div>’;
}
if (data.box_office_worldwide) {
html += ‘<div class=”financial-item”><span class=”financial-label”>🌍 Worldwide Box Office:</span><span class=”financial-value”>’ + data.box_office_worldwide + ‘</span></div>’;
}
if (data.box_office_usa) {
html += ‘<div class=”financial-item”><span class=”financial-label”>🇺🇸 USA & Canada:</span><span class=”financial-value”>’ + data.box_office_usa + ‘</span></div>’;
}
if (data.opening_weekend) {
html += ‘<div class=”financial-item”><span class=”financial-label”>🎬 Opening Weekend:</span><span class=”financial-value”>’ + data.opening_weekend + ‘</span></div>’;
}
html += ‘</div></div>’;
}

// Reviews Section
if (data.reviews && data.reviews.length > 0) {
html += ‘<div class=”cdm-section-divider”></div>’;
html += ‘<div class=”cdm-reviews-section”>’;
html += ‘<h4 class=”section-title”>⭐ Top User Reviews</h4>’;
html += ‘<div class=”reviews-grid”>’;
for (var i = 0; i < Math.min(5, data.reviews.length); i++) {
var review = data.reviews[i];
html += ‘<div class=”review-card”>’;
html += ‘<div class=”review-header”>’;
html += ‘<span class=”review-title”>’ + review.title + ‘</span>’;
if (review.rating) {
html += ‘<span class=”review-rating”>’ + review.rating + ‘</span>’;
}
html += ‘</div>’;
html += ‘<div class=”review-meta”>’;
html += ‘<span class=”review-author”>by ‘ + review.author + ‘</span>’;
if (review.date) {
html += ‘<span class=”review-date”>’ + review.date + ‘</span>’;
}
html += ‘</div>’;
html += ‘<div class=”review-content”>’;
html += ‘<div class=”review-text” id=”review-text-‘ + i + ‘”>’ + review.text + ‘</div>’;
html += ‘</div>’;
html += ‘</div>’;
}
html += ‘</div></div>’;
}

html += ‘</div>’; // End enhanced data container

// Update the display and show trailer button
$(‘#cdm-imdb-data-display’).html(html);
$(‘#cdm-fetch-trailer’).show();
}

/**
* YOUTUBE TRAILER FETCHING SYSTEM – FIXED VARIABLE NAMES
* Purpose: Fetches YouTube trailers for movies
* Features: Language selection, embed code generation
* Dependencies: YouTube API, WordPress AJAX
*/

// YouTube Trailer Fetch Button Handler
$(‘#cdm-fetch-trailer’).off(‘click’).on(‘click’, function(e) {
e.preventDefault();
e.stopImmediatePropagation();

// Prevent multiple rapid clicks
if ($(this).hasClass(‘processing’)) {
return false;
}

var postId = getPostIdRobust();
var selectedLanguage = $(‘#cdm-trailer-language’).val();

console.log(‘Trailer Post ID:’, postId);
console.log(‘Language:’, selectedLanguage);

// Validate Post ID
if (!postId || postId == ‘0’) {
alert(‘Post ID not found. Please save the draft first.’);
return false;
}

// Mark as processing
$(this).addClass(‘processing’);

var button = $(this);
var originalText = button.html();

// Update button state
button.prop(‘disabled’, true).html(‘🔍 Fetching…’);

// Execute trailer fetch AJAX request – FIXED VARIABLE NAMES
$.ajax({
url: cdmajax.ajaxurl,
type: ‘POST’,
data: {
action: ‘cdm_fetch_trailer’,
nonce: cdmajax.nonce,
post_id: postId,
language: selectedLanguage
},
success: function(response) {
console.log(‘Trailer response:’, response);

if (response.success) {
$(‘#cdm_youtube_embed’).val(response.data.embed_code).trigger(‘input’);
updateEmbedPreview(response.data.embed_code);
} else {
alert(‘Failed to fetch trailer: ‘ + (response.data || ‘Unknown error’));
}
},
error: function(xhr, status, error) {
console.log(‘AJAX Error:’, xhr.responseText);
alert(‘Network error while fetching trailer: ‘ + error);
},
complete: function() {
// Reset button state
button.prop(‘disabled’, false).html(originalText);
button.removeClass(‘processing’);
}
});

return false;
});

/**
* YOUTUBE EMBED PREVIEW SYSTEM
* Purpose: Shows live preview of YouTube embed codes
* Features: Real-time preview, responsive design
*/

// YouTube embed preview
$(‘#cdm_youtube_embed’).on(‘input’, function() {
var embedCode = $(this).val().trim();
updateEmbedPreview(embedCode);
});

function updateEmbedPreview(embedCode) {
var preview = $(‘#cdm-embed-preview’);
if (embedCode && embedCode.includes(‘iframe’)) {
if (preview.length === 0) {
$(‘#cdm_youtube_embed’).after(‘<div id=”cdm-embed-preview” class=”cdm-embed-preview”><strong>Preview:</strong><div style=”margin-top: 15px; text-align: center;”><div class=”cdm-youtube-container”></div></div></div>’);
preview = $(‘#cdm-embed-preview’);
}
preview.find(‘.cdm-youtube-container’).html(embedCode);
preview.show();
} else {
preview.hide();
}
}

/**
* Streaming Platform Fetching System – ENHANCED WITH PRIORITY AND FREE OPTIONS
* Last Update: June 08, 2025 12:50 AM GMT – Added priority URL and free options display
* Purpose: Fetches streaming platform URLs using IMDB ID with enhanced display
* Features: Priority platform in main field, free options below, persistent display
* Dependencies: WordPress AJAX, Streaming Availability API
*/

// Streaming Platform Fetch Button Handler – Enhanced for Priority and Free Options
$(‘#cdm-fetch-streaming’).off(‘click’).on(‘click’, function(e) {
e.preventDefault();
e.stopImmediatePropagation();

console.log(‘=== FETCH PLATFORM CLICKED ===’);

// Prevent multiple rapid clicks
if ($(this).hasClass(‘processing’)) {
return false;
}

var postId = getPostIdRobust();
console.log(‘Streaming Post ID:’, postId);
console.log(‘AJAX URL:’, cdmajax.ajaxurl);
console.log(‘Nonce:’, cdmajax.nonce);

// Validate Post ID
if (!postId || postId == ‘0’) {
alert(‘Post ID not found. Please save the draft first.’);
return false;
}

// Mark as processing
$(this).addClass(‘processing’);

var button = $(this);
var originalText = button.html();

// Update button state
button.prop(‘disabled’, true).html(‘🔍 Fetching Platforms…’);

// Execute streaming platform fetch AJAX request
$.ajax({
url: cdmajax.ajaxurl,
type: ‘POST’,
data: {
action: ‘cdm_fetch_streaming’,
nonce: cdmajax.nonce,
post_id: postId
},
success: function(response) {
console.log(‘Streaming response:’, response);

if (response.success) {
console.log(‘Streaming data received:’, response.data);

// Auto-fill the main streaming platform field with priority URL
if (response.data.priority_url) {
$(‘#cdm_streaming_platform’).val(response.data.priority_url).trigger(‘input’);
} else {
console.log(‘WARNING: No priority URL found in response’);
}

// Show free options display if available
if (response.data.free_display) {
console.log(‘Showing free options display’);

// Remove existing free options display
$(‘.cdm-persistent-free-options’).remove();

// Add new persistent free options display
$(‘#cdm_streaming_platform’).closest(‘.cdm-form-row’).after(
‘<div class=”cdm-persistent-free-options”>’ + response.data.free_display + ‘</div>’
);
}

// Show success message with details
var message = ‘Found priority platform’;
if (response.data.free_platforms && response.data.free_platforms.length > 0) {
message += ‘ + ‘ + response.data.free_platforms.length + ‘ free option(s)!’;
} else {
message += ‘ (no free options available)’;
}
alert(message);

} else {
alert(‘Failed to fetch streaming platforms: ‘ + (response.data || ‘Unknown error’));
}
},
error: function(xhr, status, error) {
console.log(‘AJAX Error:’, xhr.responseText);
alert(‘Network error while fetching streaming platforms: ‘ + error);
},
complete: function() {
// Reset button state
button.prop(‘disabled’, false).html(originalText);
button.removeClass(‘processing’);
}
});

return false;
});

/**
* PLAGIARISM CHECKING SYSTEM – FIXED VARIABLE NAMES
*/

// Plagiarism checking
$(‘#cdm-check-plagiarism’).click(function() {
var postId = $(‘#cdm-post-id’).val();
var text1 = $(‘#cdm_research_data’).val().trim();
var text2 = $(‘#cdm_final_edit’).val().trim();

if (!text1 || !text2) {
alert(‘Please fill in both Research Data and Final Edit sections before checking for plagiarism.’);
return;
}

var button = $(this);
var originalText = button.html();

button.prop(‘disabled’, true).html(‘<span class=”dashicons dashicons-update spin”></span> Checking…’);

$.ajax({
url: cdmajax.ajaxurl,
type: ‘POST’,
data: {
action: ‘cdm_check_plagiarism’,
nonce: cdmajax.nonce,
post_id: postId,
text1: text1,
text2: text2
},
success: function(response) {
if (response.success) {
displayPlagiarismResults(response.data);
} else {
alert(‘Plagiarism check failed: ‘ + (response.data || ‘Unknown error’));
}
},
error: function() {
alert(‘Network error during plagiarism check.’);
},
complete: function() {
button.prop(‘disabled’, false).html(originalText);
}
});
});

function displayPlagiarismResults(data) {
var html = ‘<div class=”cdm-plagiarism-results”>’;
html += ‘<h4>Plagiarism Check Results</h4>’;

if (data.matches && data.matches.length > 0) {
html += ‘<div class=”plagiarism-summary”>’;
html += ‘<p><strong>’ + data.matches.length + ‘ potential matches found</strong></p>’;
html += ‘</div>’;

html += ‘<div class=”plagiarism-highlighted”>’;
html += ‘<h5>Highlighted Text</h5>’;
html += ‘<div class=”highlighted-content”>’ + data.highlighted_text + ‘</div>’;
html += ‘</div>’;
} else {
html += ‘<div class=”plagiarism-summary success”>’;
html += ‘<p><strong>No significant plagiarism detected</strong></p>’;
html += ‘</div>’;
}

html += ‘</div>’;
$(‘#cdm-plagiarism-results’).html(html);
}

/**
* AI DETECTION SYSTEM – FIXED VARIABLE NAMES
*/

// AI Detection
$(‘#cdm-ai-detect’).click(function() {
var text = $(‘#cdm_final_edit’).val().trim();

if (!text) {
alert(‘Please fill in the Final Edit section before running AI detection.’);
return;
}

var button = $(this);
var originalText = button.html();

button.prop(‘disabled’, true).html(‘<span class=”dashicons dashicons-update spin”></span> Analyzing…’);

$.ajax({
url: cdmajax.ajaxurl,
type: ‘POST’,
data: {
action: ‘cdm_ai_detect’,
nonce: cdmajax.nonce,
text: text
},
success: function(response) {
if (response.success) {
displayAiResults(response.data);
} else {
alert(‘AI detection failed: ‘ + (response.data || ‘Unknown error’));
}
},
error: function() {
alert(‘Network error during AI detection.’);
},
complete: function() {
button.prop(‘disabled’, false).html(originalText);
}
});
});

function displayAiResults(data) {
var html = ‘<div class=”cdm-ai-results”>’;
html += ‘<h4>AI Content Detection Results</h4>’;

if (data.analysis) {
html += ‘<div class=”ai-analysis”>’;
html += ‘<pre>’ + data.analysis + ‘</pre>’;
html += ‘</div>’;
}

if (data.usage) {
html += ‘<div class=”ai-usage”>’;
html += ‘<small>Tokens used: ‘ + (data.usage.total_tokens || ‘Unknown’) + ‘</small>’;
html += ‘</div>’;
}

html += ‘</div>’;
$(‘#cdm-ai-results’).html(html);
}

/**
* FORM VALIDATION AND SETTINGS
*/

// Form validation for API keys in settings
$(‘#youtube_api_key’).on(‘input’, function() {
var key = $(this).val().trim();
var field = $(this);

if (!key) {
field.removeClass(‘valid invalid’);
} else if (key.startsWith(‘AIza’)) {
field.removeClass(‘invalid’).addClass(‘valid’);
} else {
field.removeClass(‘valid’).addClass(‘invalid’);
}
});

$(‘#groq_api_key’).on(‘input’, function() {
var key = $(this).val().trim();
var field = $(this);

if (!key) {
field.removeClass(‘valid invalid’);
} else if (key.startsWith(‘gsk_’)) {
field.removeClass(‘invalid’).addClass(‘valid’);
} else {
field.removeClass(‘valid’).addClass(‘invalid’);
}
});

// Settings form validation
$(‘.cdm-settings-form’).on(‘submit’, function(e) {
var youtubeKey = $(‘#youtube_api_key’).val().trim();
var groqKey = $(‘#groq_api_key’).val().trim();

console.log(‘Form submission – YouTube API Key:’, youtubeKey);
console.log(‘Form submission – Groq API Key:’, groqKey);

// Validate YouTube API key format
if (youtubeKey && !youtubeKey.startsWith(‘AIza’)) {
alert(‘YouTube API key should start with “AIza”‘);
$(‘#youtube_api_key’).focus();
return false;
}

// Validate Groq API key format
if (groqKey && !groqKey.startsWith(‘gsk_’)) {
alert(‘Groq API key should start with “gsk_”‘);
$(‘#groq_api_key’).focus();
return false;
}

// Show loading indicator
$(this).find(‘input[type=”submit”]’).prop(‘disabled’, true).val(‘Saving…’);
});

// Tab switching functionality for settings
$(‘.nav-tab’).click(function(e) {
e.preventDefault();

// Remove active class from all tabs and content
$(‘.nav-tab’).removeClass(‘nav-tab-active’);
$(‘.cdm-tab-content’).removeClass(‘active’);

// Add active class to clicked tab
$(this).addClass(‘nav-tab-active’);

// Show corresponding content
var tabId = $(this).data(‘tab’);
$(‘#’ + tabId).addClass(‘active’);
});

// Initialize original values for auto-save
$(‘.cdm-auto-save’).each(function() {
$(this).data(‘original-value’, $(this).val());
});

}); // End jQuery document ready

/**
* YouTube Transcript Fetching System
* Last Update: June 08, 2025 at 09:35 PM GMT – Added transcript fetching integration
* Purpose: Fetches YouTube transcripts and appends to Research Data
* Features: Language selection, progress feedback, data persistence
* Dependencies: WordPress AJAX, Python script
*/

// Show/hide transcript controls based on Sources content
$(‘#cdm_sources’).on(‘input’, function() {
var content = $(this).val().trim();
var hasYouTubeUrls = content.includes(‘youtube.com’) || content.includes(‘youtu.be’);

if (hasYouTubeUrls) {
$(‘.cdm-transcript-controls’).show();
} else {
$(‘.cdm-transcript-controls’).hide();
}
});

// YouTube Transcript Fetch Button Handler
$(‘#cdm-fetch-transcripts’).off(‘click’).on(‘click’, function(e) {
e.preventDefault();
e.stopImmediatePropagation();

// Prevent multiple rapid clicks
if ($(this).hasClass(‘processing’)) {
return false;
}

var postId = getPostIdRobust();
var sources = $(‘#cdm_sources’).val().trim();
var languagePreference = $(‘#cdm-transcript-language’).val() || ‘auto’;

console.log(‘TRANSCRIPT FETCH DEBUG START’);
console.log(‘Post ID:’, postId);
console.log(‘Sources content:’, sources);
console.log(‘Language preference:’, languagePreference);

// Validate Post ID
if (!postId || postId == 0) {
alert(‘Post ID not found. Please save the draft first.’);
return false;
}

// Validate Sources content
if (!sources) {
alert(‘Please add YouTube URLs to the Sources field first.’);
return false;
}

// Check for YouTube URLs
if (!sources.includes(‘youtube.com’) && !sources.includes(‘youtu.be’)) {
alert(‘No YouTube URLs found in Sources field. Please add YouTube URLs (one per line).’);
return false;
}

// Extract YouTube URLs from sources
var lines = sources.split(‘\n’);
var youtubeUrls = [];

lines.forEach(function(line) {
line = line.trim();
if (line.includes(‘youtube.com/watch?v=’) || line.includes(‘youtu.be/’)) {
youtubeUrls.push(line);
}
});

if (youtubeUrls.length === 0) {
alert(‘No valid YouTube URLs found. Please check your URLs format.’);
return false;
}

if (youtubeUrls.length > 5) {
alert(‘Maximum 5 YouTube URLs allowed. Please remove some URLs.’);
return false;
}

// Mark as processing
$(this).addClass(‘processing’);
var button = $(this);
var originalText = button.html();

// Update button state
button.prop(‘disabled’, true).html(‘<span class=”dashicons dashicons-update spin”></span> Fetching…’);

// Show loading indicator
$(‘#cdm-transcript-loading’).show();

console.log(‘Starting transcript fetch for’, youtubeUrls.length, ‘URLs’);

// Execute AJAX request
$.ajax({
url: cdmajax.ajaxurl,
type: ‘POST’,
data: {
action: ‘cdm_fetch_transcripts’,
nonce: cdmajax.nonce,
postid: postId,
urls: youtubeUrls.join(‘\n’),
language_preference: languagePreference
},
success: function(response) {
console.log(‘TRANSCRIPT AJAX SUCCESS’);
console.log(‘Full response:’, response);

if (response.success) {
console.log(‘Transcripts fetched successfully’);

// Update the Research Data field with the new content
$(‘#cdm_researchdata’).val(response.data.updated_research_data).trigger(‘input’);

// Show success message
alert(‘YouTube transcripts fetched successfully and added to Research Data!’);

// Hide loading indicator
$(‘#cdm-transcript-loading’).hide();

} else {
console.log(‘ERROR: Server returned failure’);
console.log(‘Error message:’, response.data);
alert(‘Failed to fetch transcripts: ‘ + (response.data || ‘Unknown error’));
}
},
error: function(xhr, status, error) {
console.log(‘TRANSCRIPT AJAX ERROR’);
console.log(‘XHR object:’, xhr);
console.log(‘Status:’, status);
console.log(‘Error:’, error);
console.log(‘Response text:’, xhr.responseText);

alert(‘Network error while fetching transcripts: ‘ + error);
},
complete: function() {
console.log(‘TRANSCRIPT AJAX COMPLETE’);
console.log(‘Resetting button state’);

// Reset button state
button.prop(‘disabled’, false).html(originalText);
button.removeClass(‘processing’);

// Hide loading indicator
$(‘#cdm-transcript-loading’).hide();

console.log(‘TRANSCRIPT FETCH DEBUG END’);
}
});

return false;
});

// YouTube Transcript Fetching System – Ends Here

/**
* Claude AI Generation System
* Last Update: June 09, 2025 – Phase 1 Implementation
* Purpose: Handles Claude AI content generation with cost calculation
* Features: Model selection, cost estimation, real-time generation
*/

// Claude Model Cost Calculation
$(‘#cdm_claude_model’).on(‘change’, function() {
calculateEstimatedCost();
});

function calculateEstimatedCost() {
var model = $(‘#cdm_claude_model’).val();
var researchData = $(‘#cdm_research_data’).val();
var personalNote = $(‘#cdm_personal_note’).val();

// Estimate token count (rough calculation: 1 token ≈ 4 characters)
var estimatedTokens = Math.ceil((researchData.length + personalNote.length + 1000) / 4);

// Model pricing per 1M tokens
var pricing = {
‘claude-3-5-sonnet-20241022’: 3.00,
‘claude-3-haiku-20240307’: 0.25,
‘claude-3-opus-20240229’: 15.00,
‘claude-3-5-haiku-20241022’: 1.00
};

var costPer1M = pricing[model] || 3.00;
var estimatedCost = (estimatedTokens / 1000000) * costPer1M;

$(‘#estimated-cost’).text(‘Estimated cost: $’ + estimatedCost.toFixed(4));
}

// Claude AI Generation Button Handler
$(‘#cdm-generate-content’).off(‘click’).on(‘click’, function(e) {
e.preventDefault();
e.stopImmediatePropagation();

// Prevent multiple rapid clicks
if ($(this).hasClass(‘processing’)) {
return false;
}

var postId = getPostIdRobust();
var model = $(‘#cdm_claude_model’).val();
var personalNote = $(‘#cdm_personal_note’).val();
var researchData = $(‘#cdm_research_data’).val();

console.log(‘=== CLAUDE GENERATION START ===’);
console.log(‘Post ID:’, postId);
console.log(‘Model:’, model);
console.log(‘Research Data Length:’, researchData.length);

// Validation
if (!postId || postId == ‘0’) {
alert(‘Post ID not found. Please save the draft first.’);
return false;
}

if (!researchData.trim()) {
alert(‘Please add research data first (Tab 2). This provides context for the AI.’);
return false;
}

if (researchData.length < 100) {
alert(‘Research data seems too short. Please add more content for better AI generation.’);
return false;
}

// Mark as processing
$(this).addClass(‘processing’);
var button = $(this);
var originalText = button.html();

// Update UI
button.prop(‘disabled’, true).html(‘<span class=”dashicons dashicons-update spin”></span> Generating with Claude…’);
$(‘#generation-progress’).show();

// Calculate final cost estimate
calculateEstimatedCost();

console.log(‘Starting Claude API request…’);

// Execute Claude generation AJAX request
$.ajax({
url: cdmajax.ajaxurl,
type: ‘POST’,
data: {
action: ‘cdm_claude_generate’,
nonce: cdmajax.nonce,
post_id: postId,
model: model,
personal_note: personalNote,
research_data: researchData
},
success: function(response) {
console.log(‘=== CLAUDE GENERATION SUCCESS ===’);
console.log(‘Response:’, response);

if (response.success) {
// Populate the generated fields
if (response.data.titles) {
$(‘#cdm_generated_titles’).val(response.data.titles).trigger(‘input’);
}

if (response.data.hashtags) {
$(‘#cdm_hashtags’).val(response.data.hashtags).trigger(‘input’);
}

if (response.data.article) {
$(‘#cdm_generated_draft’).val(response.data.article).trigger(‘input’);
}

// Show success message with cost
var message = ‘Claude AI generation completed successfully!’;
if (response.data.cost) {
message += ‘\nActual cost: $’ + response.data.cost;
}
if (response.data.tokens_used) {
message += ‘\nTokens used: ‘ + response.data.tokens_used;
}

alert(message);

console.log(‘Claude generation completed successfully’);
} else {
console.log(‘ERROR: Claude generation failed’);
console.log(‘Error message:’, response.data);
alert(‘Claude AI generation failed: ‘ + (response.data || ‘Unknown error’));
}
},
error: function(xhr, status, error) {
console.log(‘=== CLAUDE GENERATION ERROR ===’);
console.log(‘XHR:’, xhr);
console.log(‘Status:’, status);
console.log(‘Error:’, error);
console.log(‘Response Text:’, xhr.responseText);

alert(‘Network error during Claude generation: ‘ + error);
},
complete: function() {
console.log(‘=== CLAUDE GENERATION COMPLETE ===’);

// Reset button state
button.prop(‘disabled’, false).html(originalText);
button.removeClass(‘processing’);
$(‘#generation-progress’).hide();

console.log(‘=== CLAUDE GENERATION END ===’);
}
});

return false;
});

// Initialize cost calculation on page load
$(document).ready(function() {
if ($(‘#cdm_claude_model’).length > 0) {
calculateEstimatedCost();
}
});

// Claude AI Generation System – Ends Here

// CSS for spinning animation
$(‘<style type=”text/css”>’).html(`
.spin {
animation: spin 1s linear infinite;
}

@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

.cdm-plagiarism-results {
margin-top: 15px;
padding: 20px;
background: #fff;
border: 1px solid #ddd;
border-radius: 5px;
}

.plagiarism-summary.success {
color: #46b450;
}

.highlighted-content {
padding: 15px;
background: #f9f9f9;
border: 1px solid #ddd;
border-radius: 3px;
max-height: 300px;
overflow-y: auto;
}

.cdm-plagiarism-match {
background-color: #ffeb3b;
padding: 2px 4px;
border-radius: 2px;
cursor: help;
}

.cdm-match-high {
background-color: #f44336;
color: white;
}

.cdm-match-medium {
background-color: #ff9800;
color: white;
}

.cdm-match-low {
background-color: #ffeb3b;
color: black;
}

.cdm-ai-results {
margin-top: 15px;
padding: 20px;
background: #fff;
border: 1px solid #ddd;
border-radius: 5px;
}

.ai-analysis pre {
white-space: pre-wrap;
word-wrap: break-word;
background: #f8f9fa;
padding: 15px;
border-radius: 3px;
border: 1px solid #e9ecef;
}

/* Persistent Free Options Styling */
.cdm-persistent-free-options {
margin-top: 15px;
padding: 15px;
background: rgba(255, 255, 255, 0.95);
border-radius: 6px;
border-left: 4px solid #27ae60;
}

.free-options-title {
font-size: 14px;
font-weight: 600;
color: #2c3e50;
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 8px;
}

.free-options-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 12px;
}

.free-option-item {
padding: 10px;
background: white;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
border-left: 3px solid #27ae60;
transition: transform 0.2s ease;
}

.free-option-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}

.free-platform-info {
font-weight: 600;
color: #2c3e50;
margin-bottom: 5px;
}

.free-platform-details {
font-size: 12px;
color: #7f8c8d;
line-height: 1.4;
}

.free-platform-details a {
color: #27ae60;
text-decoration: none;
font-weight: 500;
}

.free-platform-details a:hover {
text-decoration: underline;
}

/* Responsive design for free options */
@media (max-width: 768px) {
.free-options-grid {
grid-template-columns: 1fr;
gap: 8px;
}

.free-option-item {
padding: 8px;
}
}
`).appendTo(‘head’);

### class-imdb-fetcher.php
✅ Direct cURL implementation with error handling

<?php
/**
* STABLE IMDB Data Fetcher – Enhanced with TMDB Integration
* Focuses on reliability with TMDB enhancement for cast and reviews
*/

class CDM_IMDB_Fetcher {

private static $poster_dir = ‘cdm-posters’;

public static function fetch_movie_data($imdb_url, $post_id) {
// CRITICAL: Validate post ID first
if (empty($post_id) || $post_id == 0) {
return array(‘error’ => ‘Post ID is required’);
}

// Ultra-strict timeout for stability
ini_set(‘max_execution_time’, 20);
set_time_limit(20);

// Extract IMDB ID from URL
preg_match(‘/title\/(tt\d+)/’, $imdb_url, $matches);
if (!$matches) {
return array(‘error’ => ‘Invalid IMDB URL’);
}

$imdb_id = $matches[1];

// STABLE: Basic data extraction only
$start_time = time();
try {
$movie_data = self::fetch_stable_data($imdb_url, $imdb_id, $start_time);
} catch (Exception $e) {
$movie_data = self::get_fallback_data($imdb_url);
}

// Final timeout check
if ((time() – $start_time) > 15) {
$movie_data = self::get_fallback_data($imdb_url);
}

if (!isset($movie_data[‘error’])) {
// TMDB Enhancement – only if TMDB API is configured
if (!empty(get_option(‘cdm_tmdb_api_key’))) {
error_log(‘CDM: Enhancing with TMDB data…’);
$movie_data = CDM_TMDB_Fetcher::enhance_movie_data($movie_data, $imdb_id);
} else {
error_log(‘CDM: TMDB API key not configured – using IMDB-only data’);
}

// Save poster if available
if (!empty($movie_data[‘poster’])) {
$local_poster = self::save_poster($movie_data[‘poster’], $imdb_id);
if ($local_poster) {
$movie_data[‘local_poster’] = $local_poster;
}
}

// Save to post meta
self::save_to_post_meta($post_id, $movie_data);

// Update post with complete data
wp_update_post(array(
‘ID’ => $post_id,
‘post_title’ => $movie_data[‘title’] . ‘ (‘ . $movie_data[‘year’] . ‘) – Movie Draft’,
‘post_content’ => self::generate_complete_post_content($movie_data),
‘post_status’ => ‘draft’,
‘post_excerpt’ => substr($movie_data[‘plot’] ?? ‘Movie draft’, 0, 155)
));
}

return $movie_data;
}

// STABLE: Basic data extraction with working cast and reviews placeholders
private static function fetch_stable_data($url, $imdb_id, $start_time) {
$max_time = 12; // 12 seconds max

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_USERAGENT, ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36’);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

$html = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// Timeout check
if ((time() – $start_time) > $max_time || $http_code !== 200 || !$html) {
return self::get_fallback_data($url);
}

$movie_data = array();

// Extract JSON-LD data (most reliable)
if (preg_match(‘/<script type=”application\/ld\+json”>([^<]+)<\/script>/’, $html, $json_matches)) {
$json_data = json_decode($json_matches[1], true);

if ($json_data) {
$movie_data[‘title’] = $json_data[‘name’] ?? ”;
$movie_data[‘year’] = isset($json_data[‘datePublished’]) ? substr($json_data[‘datePublished’], 0, 4) : ”;
$movie_data[‘plot’] = $json_data[‘description’] ?? ”;
$movie_data[‘poster’] = $json_data[‘image’] ?? ”;
$movie_data[‘duration’] = $json_data[‘duration’] ?? ”;

// Rating
if (isset($json_data[‘aggregateRating’][‘ratingValue’])) {
$movie_data[‘rating’] = $json_data[‘aggregateRating’][‘ratingValue’];
}

// Director (IMDB fallback if TMDB fails)
if (isset($json_data[‘director’])) {
if (is_array($json_data[‘director’])) {
$movie_data[‘director’] = $json_data[‘director’][0][‘name’] ?? ”;
} else {
$movie_data[‘director’] = $json_data[‘director’][‘name’] ?? ”;
}
}

// Writer (IMDB fallback if TMDB fails)
if (isset($json_data[‘creator’])) {
if (is_array($json_data[‘creator’])) {
$movie_data[‘writer’] = $json_data[‘creator’][0][‘name’] ?? ”;
} else {
$movie_data[‘writer’] = $json_data[‘creator’][‘name’] ?? ”;
}
}

// Genres
if (isset($json_data[‘genre’]) && is_array($json_data[‘genre’])) {
$movie_data[‘genres’] = array_slice($json_data[‘genre’], 0, 5);
}

// Country
if (isset($json_data[‘countryOfOrigin’])) {
$country = is_array($json_data[‘countryOfOrigin’])
? $json_data[‘countryOfOrigin’][0][‘name’] ?? ”
: $json_data[‘countryOfOrigin’][‘name’] ?? ”;
$movie_data[‘country’] = $country;
}

// Production Company
if (isset($json_data[‘productionCompany’])) {
$production = is_array($json_data[‘productionCompany’])
? $json_data[‘productionCompany’][0][‘name’] ?? ”
: $json_data[‘productionCompany’][‘name’] ?? ”;
$movie_data[‘production’] = $production;
}
}
}

// Extract financial data
self::extract_financial_data_stable($html, $movie_data);

// STABLE: Add proper cast and reviews placeholders for layout (will be enhanced by TMDB)
$movie_data[‘top_cast’] = array(
array(‘name’ => ‘Cast data will be enhanced’, ‘character’ => ‘with TMDB API integration’),
array(‘name’ => ‘Current layout preserved’, ‘character’ => ‘for stable backup version’)
);

$movie_data[‘reviews’] = array(
array(
‘rating’ => ‘8/10’,
‘title’ => ‘Reviews will be enhanced with TMDB’,
‘text’ => ‘The current stable version preserves the layout structure while we prepare for TMDB API integration for comprehensive cast and reviews data.’,
‘author’ => ‘System’,
‘date’ => date(‘Y-m-d’)
)
);

return $movie_data;
}

// Extract financial data
private static function extract_financial_data_stable($html, &$movie_data) {
// Budget
if (preg_match(‘/Budget.*?\$([0-9,]+(?:\.[0-9]+)?(?:\s*million)?)/i’, $html, $matches)) {
$movie_data[‘budget’] = ‘$’ . trim($matches[1]);
}

// Worldwide box office
if (preg_match(‘/Gross worldwide.*?\$([0-9,]+(?:\.[0-9]+)?(?:\s*million)?)/i’, $html, $matches)) {
$movie_data[‘box_office_worldwide’] = ‘$’ . trim($matches[1]);
}

// USA box office
if (preg_match(‘/Gross USA & Canada.*?\$([0-9,]+(?:\.[0-9]+)?(?:\s*million)?)/i’, $html, $matches)) {
$movie_data[‘box_office_usa’] = ‘$’ . trim($matches[1]);
}

// Opening weekend
if (preg_match(‘/Opening weekend USA.*?\$([0-9,]+(?:\.[0-9]+)?(?:\s*million)?)/i’, $html, $matches)) {
$movie_data[‘opening_weekend’] = ‘$’ . trim($matches[1]);
}
}

// Fallback data
private static function get_fallback_data($url) {
preg_match(‘/title\/(tt\d+)/’, $url, $matches);
$imdb_id = $matches[1] ?? ‘unknown’;

return array(
‘title’ => ‘Movie Title (Fetching Failed)’,
‘year’ => date(‘Y’),
‘plot’ => ‘Movie information could not be fetched from IMDB.’,
‘rating’ => ‘0’,
‘director’ => ‘Unknown Director’,
‘writer’ => ‘Unknown Writer’,
‘genres’ => array(‘Unknown’),
‘country’ => ‘Unknown’,
‘production’ => ‘Unknown’,
‘top_cast’ => array(),
‘reviews’ => array(),
‘budget’ => ‘Unknown’,
‘box_office_worldwide’ => ‘Unknown’
);
}

// Generate complete post content
private static function generate_complete_post_content($movie_data) {
$content = “Movie Draft: ” . $movie_data[‘title’] . ” (” . $movie_data[‘year’] . “)\n\n”;
$content .= “Director: ” . ($movie_data[‘director’] ?? ‘Unknown’) . “\n”;
$content .= “Writer: ” . ($movie_data[‘writer’] ?? ‘Unknown’) . “\n”;
$content .= “Plot: ” . ($movie_data[‘plot’] ?? ‘Plot not available’) . “\n\n”;
$content .= “Draft created via Content Draft Manager plugin on ” . date(‘Y-m-d H:i:s’);
return $content;
}

private static function save_to_post_meta($post_id, $movie_data) {
update_post_meta($post_id, ‘_cdm_movie_title’, $movie_data[‘title’] ?? ”);
update_post_meta($post_id, ‘_cdm_movie_year’, $movie_data[‘year’] ?? ”);
update_post_meta($post_id, ‘_cdm_movie_duration’, $movie_data[‘duration’] ?? ”);
update_post_meta($post_id, ‘_cdm_movie_plot’, $movie_data[‘plot’] ?? ”);
update_post_meta($post_id, ‘_cdm_movie_rating’, $movie_data[‘rating’] ?? ”);
update_post_meta($post_id, ‘_cdm_movie_poster’, $movie_data[‘poster’] ?? ”);
update_post_meta($post_id, ‘_cdm_movie_local_poster’, $movie_data[‘local_poster’] ?? ”);
update_post_meta($post_id, ‘_cdm_movie_director’, $movie_data[‘director’] ?? ”);
update_post_meta($post_id, ‘_cdm_movie_writer’, $movie_data[‘writer’] ?? ”);
update_post_meta($post_id, ‘_cdm_movie_top_cast’, $movie_data[‘top_cast’] ?? array());
update_post_meta($post_id, ‘_cdm_movie_genres’, $movie_data[‘genres’] ?? array());
update_post_meta($post_id, ‘_cdm_movie_country’, $movie_data[‘country’] ?? ”);
update_post_meta($post_id, ‘_cdm_movie_production’, $movie_data[‘production’] ?? ”);
update_post_meta($post_id, ‘_cdm_movie_reviews’, $movie_data[‘reviews’] ?? array());
update_post_meta($post_id, ‘_cdm_movie_budget’, $movie_data[‘budget’] ?? ”);
update_post_meta($post_id, ‘_cdm_movie_box_office_worldwide’, $movie_data[‘box_office_worldwide’] ?? ”);
update_post_meta($post_id, ‘_cdm_movie_box_office_usa’, $movie_data[‘box_office_usa’] ?? ”);
update_post_meta($post_id, ‘_cdm_movie_opening_weekend’, $movie_data[‘opening_weekend’] ?? ”);
}

public static function get_saved_movie_data($post_id) {
return array(
‘title’ => get_post_meta($post_id, ‘_cdm_movie_title’, true),
‘year’ => get_post_meta($post_id, ‘_cdm_movie_year’, true),
‘duration’ => get_post_meta($post_id, ‘_cdm_movie_duration’, true),
‘plot’ => get_post_meta($post_id, ‘_cdm_movie_plot’, true),
‘rating’ => get_post_meta($post_id, ‘_cdm_movie_rating’, true),
‘poster’ => get_post_meta($post_id, ‘_cdm_movie_poster’, true),
‘local_poster’ => get_post_meta($post_id, ‘_cdm_movie_local_poster’, true),
‘director’ => get_post_meta($post_id, ‘_cdm_movie_director’, true),
‘writer’ => get_post_meta($post_id, ‘_cdm_movie_writer’, true),
‘top_cast’ => get_post_meta($post_id, ‘_cdm_movie_top_cast’, true),
‘genres’ => get_post_meta($post_id, ‘_cdm_movie_genres’, true),
‘country’ => get_post_meta($post_id, ‘_cdm_movie_country’, true),
‘production’ => get_post_meta($post_id, ‘_cdm_movie_production’, true),
‘reviews’ => get_post_meta($post_id, ‘_cdm_movie_reviews’, true),
‘budget’ => get_post_meta($post_id, ‘_cdm_movie_budget’, true),
‘box_office_worldwide’ => get_post_meta($post_id, ‘_cdm_movie_box_office_worldwide’, true),
‘box_office_usa’ => get_post_meta($post_id, ‘_cdm_movie_box_office_usa’, true),
‘opening_weekend’ => get_post_meta($post_id, ‘_cdm_movie_opening_weekend’, true)
);
}

private static function save_poster($poster_url, $imdb_id) {
if (empty($poster_url)) {
return false;
}

// Create poster directory
$upload_dir = wp_upload_dir();
$poster_dir = $upload_dir[‘basedir’] . ‘/’ . self::$poster_dir;

if (!file_exists($poster_dir)) {
wp_mkdir_p($poster_dir);
}

// Download poster with timeout
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $poster_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_USERAGENT, ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36’);
curl_setopt($ch, CURLOPT_TIMEOUT, 8);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);

$image_data = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($http_code === 200 && $image_data) {
$filename = $imdb_id . ‘.jpg’;
$filepath = $poster_dir . ‘/’ . $filename;

if (file_put_contents($filepath, $image_data)) {
return $upload_dir[‘baseurl’] . ‘/’ . self::$poster_dir . ‘/’ . $filename;
}
}

return false;
}

public static function fetch_youtube_trailer($movie_title, $year = ”) {
$api_key = get_option(‘cdm_youtube_api_key’);
if (empty($api_key)) {
return array(‘error’ => ‘YouTube API key not configured’);
}

// Build search query
$search_query = $movie_title;
if ($year) {
$search_query .= ‘ ‘ . $year;
}
$search_query .= ‘ official trailer’;

$api_url = ‘https://www.googleapis.com/youtube/v3/search?’ . http_build_query(array(
‘part’ => ‘snippet’,
‘q’ => $search_query,
‘type’ => ‘video’,
‘maxResults’ => 1,
‘key’ => $api_key
));

$response = wp_remote_get($api_url);

if (is_wp_error($response)) {
return array(‘error’ => ‘Failed to fetch YouTube data’);
}

$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);

if (isset($data[‘items’][0])) {
$video = $data[‘items’][0];
$video_id = $video[‘id’][‘videoId’];

// Generate embed code
$embed_code = ‘<iframe width=”560″ height=”315″ src=”https://www.youtube.com/embed/’ . $video_id . ‘” frameborder=”0″ allowfullscreen></iframe>’;

return array(
‘video_id’ => $video_id,
‘title’ => $video[‘snippet’][‘title’],
’embed_code’ => $embed_code
);
}

return array(‘error’ => ‘No trailer found’);
}
}

### class-tmdb-fetcher.php
✅ TMDB API integration for cast and streaming data

<?php

class CDM_TMDB_Fetcher {

private static $api_key = null;
private static $base_url = ‘https://api.themoviedb.org/3’;

public static function init() {
self::$api_key = get_option(‘cdm_tmdb_api_key’);
}

/**
* Convert IMDB ID to TMDB ID
*/
public static function get_tmdb_id_from_imdb($imdb_id) {
if (empty(self::$api_key)) {
error_log(‘CDM: TMDB API key not configured’);
return false;
}

$find_url = self::$base_url . “/find/{$imdb_id}?api_key=” . self::$api_key . “&external_source=imdb_id”;

$response = wp_remote_get($find_url, array(‘timeout’ => 10));
if (is_wp_error($response)) {
error_log(‘CDM: TMDB find request failed: ‘ . $response->get_error_message());
return false;
}

$data = json_decode(wp_remote_retrieve_body($response), true);

// Check for movie results first, then TV results
if (!empty($data[‘movie_results’])) {
error_log(‘CDM: Found TMDB movie ID: ‘ . $data[‘movie_results’][0][‘id’]);
return array(‘id’ => $data[‘movie_results’][0][‘id’], ‘type’ => ‘movie’);
} elseif (!empty($data[‘tv_results’])) {
error_log(‘CDM: Found TMDB TV ID: ‘ . $data[‘tv_results’][0][‘id’]);
return array(‘id’ => $data[‘tv_results’][0][‘id’], ‘type’ => ‘tv’);
}

error_log(‘CDM: No TMDB ID found for IMDB ID: ‘ . $imdb_id);
return false;
}

/**
* ========================================
* ENHANCE MOVIE DATA WITH ALL TMDB FEATURES
* ========================================
* Purpose: Adds TMDB enhancements to existing movie information
* What it does: Integrates cast, crew, reviews, and streaming platforms
* ========================================
*/
public static function enhance_movie_data($movie_data, $imdb_id) {
self::init();

if (empty(self::$api_key)) {
error_log(‘CDM: TMDB API key not configured – skipping enhancement’);
return $movie_data;
}

$tmdb_info = self::get_tmdb_id_from_imdb($imdb_id);
if (!$tmdb_info) {
error_log(‘CDM: Could not find TMDB ID for IMDB ID: ‘ . $imdb_id);
return $movie_data;
}

$tmdb_id = $tmdb_info[‘id’];
$content_type = $tmdb_info[‘type’];

// Fetch director and writer
$crew_data = self::fetch_crew_data($tmdb_id, $content_type);
if ($crew_data) {
if (!empty($crew_data[‘director’])) {
$movie_data[‘director’] = $crew_data[‘director’];
error_log(‘CDM: Enhanced director from TMDB: ‘ . $crew_data[‘director’]);
}
if (!empty($crew_data[‘writer’])) {
$movie_data[‘writer’] = $crew_data[‘writer’];
error_log(‘CDM: Enhanced writer from TMDB: ‘ . $crew_data[‘writer’]);
}
}

// Fetch top billed cast
$cast_data = self::fetch_cast_data($tmdb_id, $content_type);
if ($cast_data) {
$movie_data[‘top_cast’] = $cast_data;
error_log(‘CDM: Enhanced cast data from TMDB – ‘ . count($cast_data) . ‘ actors’);
}

// Fetch reviews
$reviews_data = self::fetch_reviews_data($tmdb_id, $content_type);
if ($reviews_data) {
$movie_data[‘reviews’] = $reviews_data;
error_log(‘CDM: Enhanced reviews data from TMDB – ‘ . count($reviews_data) . ‘ reviews’);
}

// Fetch streaming platforms data
$streaming_data = self::fetch_streaming_data($tmdb_id, $content_type);
if ($streaming_data) {
$movie_data[‘streaming_platforms’] = $streaming_data;
$total_platforms = 0;
foreach ($streaming_data as $category) {
$total_platforms += count($category);
}
error_log(‘CDM: Enhanced streaming data from TMDB – ‘ . $total_platforms . ‘ platforms found’);
}

return $movie_data;
}

/**
* Fetch crew data (director, writer)
*/
private static function fetch_crew_data($tmdb_id, $content_type) {
$credits_url = self::$base_url . “/{$content_type}/{$tmdb_id}/credits?api_key=” . self::$api_key;

$response = wp_remote_get($credits_url, array(‘timeout’ => 10));
if (is_wp_error($response)) {
error_log(‘CDM: TMDB credits request failed: ‘ . $response->get_error_message());
return false;
}

$data = json_decode(wp_remote_retrieve_body($response), true);
$crew_data = array();

if (isset($data[‘crew’])) {
foreach ($data[‘crew’] as $crew_member) {
if ($crew_member[‘job’] === ‘Director’ && empty($crew_data[‘director’])) {
$crew_data[‘director’] = $crew_member[‘name’];
}
if (in_array($crew_member[‘job’], array(‘Writer’, ‘Screenplay’, ‘Story’)) && empty($crew_data[‘writer’])) {
$crew_data[‘writer’] = $crew_member[‘name’];
}
}
}

return $crew_data;
}

/**
* Fetch cast data
*/
private static function fetch_cast_data($tmdb_id, $content_type) {
$credits_url = self::$base_url . “/{$content_type}/{$tmdb_id}/credits?api_key=” . self::$api_key;

$response = wp_remote_get($credits_url, array(‘timeout’ => 10));
if (is_wp_error($response)) {
error_log(‘CDM: TMDB cast request failed: ‘ . $response->get_error_message());
return false;
}

$data = json_decode(wp_remote_retrieve_body($response), true);
$cast = array();

if (isset($data[‘cast’])) {
foreach (array_slice($data[‘cast’], 0, 15) as $actor) {
$cast[] = array(
‘name’ => $actor[‘name’],
‘character’ => $actor[‘character’],
‘profile_path’ => $actor[‘profile_path’] ? ‘https://image.tmdb.org/t/p/w185’ . $actor[‘profile_path’] : ”
);
}
}

return $cast;
}

/**
* Fetch reviews data with expandable text
*/
private static function fetch_reviews_data($tmdb_id, $content_type) {
$reviews_url = self::$base_url . “/{$content_type}/{$tmdb_id}/reviews?api_key=” . self::$api_key;

$response = wp_remote_get($reviews_url, array(‘timeout’ => 10));
if (is_wp_error($response)) {
error_log(‘CDM: TMDB reviews request failed: ‘ . $response->get_error_message());
return false;
}

$data = json_decode(wp_remote_retrieve_body($response), true);
$reviews = array();

if (isset($data[‘results’])) {
foreach (array_slice($data[‘results’], 0, 10) as $review) {
$full_text = $review[‘content’];
$preview_text = strlen($full_text) > 300 ? substr($full_text, 0, 300) : $full_text;

$reviews[] = array(
‘rating’ => isset($review[‘author_details’][‘rating’]) ? $review[‘author_details’][‘rating’] . ‘/10’ : ‘No rating’,
‘title’ => ‘TMDB User Review’,
‘author’ => $review[‘author’],
‘text’ => $preview_text, // Short version for initial display
‘full_text’ => $full_text, // Full version for expansion
‘is_truncated’ => strlen($full_text) > 300, // Flag to show if expandable
‘date’ => date(‘Y-m-d’, strtotime($review[‘created_at’]))
);
}
}

return $reviews;
}

/**
* ========================================
* FETCH STREAMING PLATFORMS DATA
* ========================================
* Purpose: Retrieves streaming platform availability from TMDB
* What it does: Gets where users can watch content (Netflix, Disney+, etc.)
* Features:
* – Supports multiple countries (US, GB, CA, AU)
* – Categorizes by type: streaming, rental, purchase
* – Includes platform logos for visual display
* – Prioritizes US data, falls back to other countries
* Dependencies: TMDB API key, valid movie/TV ID
* Returns: Array of streaming platforms with logos and metadata
* ========================================
*/
private static function fetch_streaming_data($tmdb_id, $content_type) {
// Build API URL for watch providers endpoint
$providers_url = self::$base_url . “/{$content_type}/{$tmdb_id}/watch/providers?api_key=” . self::$api_key;

// Execute API request with timeout protection
$response = wp_remote_get($providers_url, array(‘timeout’ => 10));
if (is_wp_error($response)) {
error_log(‘CDM: TMDB streaming providers request failed: ‘ . $response->get_error_message());
return false;
}

// Parse JSON response
$data = json_decode(wp_remote_retrieve_body($response), true);
$streaming_data = array();

if (isset($data[‘results’])) {
// Priority countries (customize based on your audience)
$priority_countries = array(‘US’, ‘GB’, ‘CA’, ‘AU’);

// Loop through countries to find streaming data
foreach ($priority_countries as $country) {
if (isset($data[‘results’][$country])) {
$country_data = $data[‘results’][$country];

// STREAMING PLATFORMS (subscription services like Netflix, Disney+)
if (isset($country_data[‘flatrate’])) {
foreach ($country_data[‘flatrate’] as $provider) {
$streaming_data[‘streaming’][] = array(
‘name’ => $provider[‘provider_name’],
‘logo’ => ‘https://image.tmdb.org/t/p/w45’ . $provider[‘logo_path’],
‘country’ => $country,
‘type’ => ‘streaming’
);
}
}

// RENTAL PLATFORMS (pay-per-view like Apple TV, Google Play)
if (isset($country_data[‘rent’])) {
foreach ($country_data[‘rent’] as $provider) {
$streaming_data[‘rental’][] = array(
‘name’ => $provider[‘provider_name’],
‘logo’ => ‘https://image.tmdb.org/t/p/w45’ . $provider[‘logo_path’],
‘country’ => $country,
‘type’ => ‘rental’
);
}
}

// PURCHASE PLATFORMS (buy to own like iTunes, Amazon)
if (isset($country_data[‘buy’])) {
foreach ($country_data[‘buy’] as $provider) {
$streaming_data[‘purchase’][] = array(
‘name’ => $provider[‘provider_name’],
‘logo’ => ‘https://image.tmdb.org/t/p/w45’ . $provider[‘logo_path’],
‘country’ => $country,
‘type’ => ‘purchase’
);
}
}

// Use first country with data (prioritizes US)
if (!empty($streaming_data)) {
break;
}
}
}
}

return $streaming_data;
}
}

### class-groq-ai-detector.php
✅ AI detection functionality implemented

<?php
class CDM_Groq_AI_Detector {

private $api_key;
private $api_url = ‘https://api.groq.com/openai/v1/chat/completions’;
private $model = ‘deepseek-r1-distill-llama-70b’;

public function __construct() {
$this->api_key = get_option(‘cdm_groq_api_key’, ”);
}

public function detect_ai_content($text) {
if (empty($this->api_key)) {
return [‘error’ => ‘Groq API key not configured’];
}

if (empty($text)) {
return [‘error’ => ‘No text provided for analysis’];
}

$prompt = $this->get_ai_detection_prompt($text);

$response = wp_remote_post($this->api_url, [
‘headers’ => [
‘Authorization’ => ‘Bearer ‘ . $this->api_key,
‘Content-Type’ => ‘application/json’,
],
‘body’ => json_encode([
‘messages’ => [
[
‘role’ => ‘system’,
‘content’ => ‘You are an expert AI content detector. Analyze the provided text and determine if it was written by AI or human. Respond ONLY with the Final Answer, Rating, and Conclusion sections as requested.’
],
[
‘role’ => ‘user’,
‘content’ => $prompt
]
],
‘model’ => $this->model,
‘temperature’ => 0.3,
‘max_completion_tokens’ => 1024,
‘top_p’ => 0.95,
‘stream’ => false
]),
‘timeout’ => 30
]);

if (is_wp_error($response)) {
return [‘error’ => ‘Failed to connect to Groq API: ‘ . $response->get_error_message()];
}

$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);

if (isset($data[‘error’])) {
return [‘error’ => ‘Groq API Error: ‘ . $data[‘error’][‘message’]];
}

if (!isset($data[‘choices’][0][‘message’][‘content’])) {
return [‘error’ => ‘Invalid response from Groq API’];
}

$raw_analysis = $data[‘choices’][0][‘message’][‘content’];
$filtered_analysis = $this->extract_key_sections($raw_analysis);

return [
‘success’ => true,
‘analysis’ => $filtered_analysis,
‘raw_analysis’ => $raw_analysis, // Keep raw for debugging
‘usage’ => $data[‘usage’] ?? null
];
}

private function extract_key_sections($text) {
// Extract only Final Answer, Rating, and Conclusion sections
$sections = [];

// Extract Final Answer
if (preg_match(‘/\*\*Final Answer:\*\*(.*?)(?=\*\*Rating|\*\*Conclusion|$)/s’, $text, $matches)) {
$sections[‘final_answer’] = trim($matches[1]);
}

// Extract Rating
if (preg_match(‘/\*\*Rating:\*\*(.*?)(?=\*\*Final Answer|\*\*Conclusion|$)/s’, $text, $matches)) {
$sections[‘rating’] = trim($matches[1]);
}

// Extract Conclusion
if (preg_match(‘/\*\*Conclusion:\*\*(.*?)(?=\*\*Final Answer|\*\*Rating|$)/s’, $text, $matches)) {
$sections[‘conclusion’] = trim($matches[1]);
}

// If sections are found, format them nicely
if (!empty($sections)) {
$formatted = ”;

if (isset($sections[‘final_answer’])) {
$formatted .= “**Final Answer:**\n” . $sections[‘final_answer’] . “\n\n”;
}

if (isset($sections[‘rating’])) {
$formatted .= “**Rating:**” . $sections[‘rating’] . “\n\n”;
}

if (isset($sections[‘conclusion’])) {
$formatted .= “**Conclusion:**\n” . $sections[‘conclusion’];
}

return trim($formatted);
}

// Fallback: if sections not found, try to clean up the response
return $this->clean_response($text);
}

private function clean_response($text) {
// Remove the original prompt text if it appears in the response
$text = preg_replace(‘/Please analyze the following text.*?ADD ARTICLE HERE/s’, ”, $text);

// Remove any remaining instruction text
$text = preg_replace(‘/Consider factors like:.*?Overall authenticity/s’, ”, $text);

// Clean up extra whitespace
$text = preg_replace(‘/\n{3,}/’, “\n\n”, $text);

return trim($text);
}

private function get_ai_detection_prompt($text) {
return “Please analyze the following text to determine if it was written by AI or a human. Consider factors like:

1. Writing style and tone
2. Sentence structure and variety
3. Use of clichés or repetitive patterns
4. Depth of personal insight or opinion
5. Natural flow and coherence
6. Grammatical patterns
7. Vocabulary choices
8. Overall authenticity

Provide your analysis in the following format:

**Final Answer:**
[Your detailed analysis of whether this appears to be AI or human-written]

**Rating:** [Scale from 1-5 where 1 = Definitely AI, 2 = Likely AI, 3 = Mixed/Uncertain, 4 = Likely Human, 5 = Definitely Human]

**Conclusion:**
[Brief summary of your reasoning]

ADD ARTICLE HERE

$text”;
}
}

### class-plagiarism-detector.php
✅ Plagiarism checking system operational

<?php

class CDM_Plagiarism_Detector {

private $min_words;
private $max_phrase_length;
private $similarity_threshold;

public function __construct() {
$this->min_words = get_option(‘cdm_plagiarism_min_words’, 5);
$this->max_phrase_length = 15;
$this->similarity_threshold = 0.8;
}

/**
* Main function to compare two texts and find matching phrases
*/
public function compare_texts($text1, $text2, $min_words = null) {
$min_words = $min_words ?: $this->min_words;

if (empty($text1) || empty($text2)) {
return [];
}

$matches = [];

// Clean and normalize both texts
$text1_clean = $this->clean_text($text1);
$text2_clean = $this->clean_text($text2);

// Split into words
$words1 = explode(‘ ‘, $text1_clean);
$words2 = explode(‘ ‘, $text2_clean);

// Find exact phrase matches
$exact_matches = $this->find_exact_matches($words1, $words2, $min_words);

// Find fuzzy matches (similar but not identical)
$fuzzy_matches = $this->find_fuzzy_matches($text1_clean, $text2_clean, $min_words);

// Combine and deduplicate matches
$all_matches = array_merge($exact_matches, $fuzzy_matches);
$matches = $this->remove_overlapping_matches($all_matches);

// Sort by severity (high to low)
usort($matches, function($a, $b) {
$severity_order = [‘high’ => 3, ‘medium’ => 2, ‘low’ => 1];
return $severity_order[$b[‘severity’]] – $severity_order[$a[‘severity’]];
});

return $matches;
}

/**
* Find exact phrase matches between two text arrays
*/
private function find_exact_matches($words1, $words2, $min_words) {
$matches = [];
$text2_string = implode(‘ ‘, $words2);

for ($i = 0; $i <= count($words1) – $min_words; $i++) {
for ($length = $min_words; $length <= min($this->max_phrase_length, count($words1) – $i); $length++) {
$phrase = implode(‘ ‘, array_slice($words1, $i, $length));

// Skip very short phrases or common words
if (strlen($phrase) < 20 || $this->is_common_phrase($phrase)) {
continue;
}

// Check if phrase exists in text2
if (strpos($text2_string, $phrase) !== false) {
$matches[] = [
‘phrase’ => $phrase,
‘position’ => $i,
‘length’ => $length,
‘severity’ => $this->calculate_severity($length, ‘exact’),
‘type’ => ‘exact’,
‘confidence’ => 100
];
}
}
}

return $matches;
}

/**
* Find fuzzy/similar matches using similarity algorithms
*/
private function find_fuzzy_matches($text1, $text2, $min_words) {
$matches = [];

// Split texts into sentences
$sentences1 = $this->split_into_sentences($text1);
$sentences2 = $this->split_into_sentences($text2);

foreach ($sentences1 as $sentence1) {
$words1 = explode(‘ ‘, $sentence1);

// Skip short sentences
if (count($words1) < $min_words) {
continue;
}

foreach ($sentences2 as $sentence2) {
$similarity = $this->calculate_text_similarity($sentence1, $sentence2);

if ($similarity >= $this->similarity_threshold) {
$matches[] = [
‘phrase’ => $sentence1,
‘similar_to’ => $sentence2,
‘position’ => 0,
‘length’ => count($words1),
‘severity’ => $this->calculate_severity(count($words1), ‘fuzzy’),
‘type’ => ‘fuzzy’,
‘confidence’ => round($similarity * 100)
];
}
}
}

return $matches;
}

/**
* Calculate text similarity using multiple algorithms
*/
private function calculate_text_similarity($text1, $text2) {
// Levenshtein similarity
$levenshtein = 1 – (levenshtein($text1, $text2) / max(strlen($text1), strlen($text2)));

// Jaccard similarity (word-based)
$words1 = array_unique(explode(‘ ‘, $text1));
$words2 = array_unique(explode(‘ ‘, $text2));
$intersection = count(array_intersect($words1, $words2));
$union = count(array_unique(array_merge($words1, $words2)));
$jaccard = $union > 0 ? $intersection / $union : 0;

// Cosine similarity (simplified)
$cosine = $this->cosine_similarity($text1, $text2);

// Weighted average
return ($levenshtein * 0.4) + ($jaccard * 0.3) + ($cosine * 0.3);
}

/**
* Simple cosine similarity implementation
*/
private function cosine_similarity($text1, $text2) {
$words1 = array_count_values(explode(‘ ‘, $text1));
$words2 = array_count_values(explode(‘ ‘, $text2));

$all_words = array_unique(array_merge(array_keys($words1), array_keys($words2)));

$vector1 = [];
$vector2 = [];

foreach ($all_words as $word) {
$vector1[] = isset($words1[$word]) ? $words1[$word] : 0;
$vector2[] = isset($words2[$word]) ? $words2[$word] : 0;
}

$dot_product = 0;
$magnitude1 = 0;
$magnitude2 = 0;

for ($i = 0; $i < count($vector1); $i++) {
$dot_product += $vector1[$i] * $vector2[$i];
$magnitude1 += $vector1[$i] * $vector1[$i];
$magnitude2 += $vector2[$i] * $vector2[$i];
}

$magnitude1 = sqrt($magnitude1);
$magnitude2 = sqrt($magnitude2);

if ($magnitude1 == 0 || $magnitude2 == 0) {
return 0;
}

return $dot_product / ($magnitude1 * $magnitude2);
}

/**
* Split text into sentences
*/
private function split_into_sentences($text) {
// Simple sentence splitting – can be improved with more sophisticated methods
$sentences = preg_split(‘/[.!?]+/’, $text, -1, PREG_SPLIT_NO_EMPTY);
return array_map(‘trim’, $sentences);
}

/**
* Check if a phrase is too common to be considered plagiarism
*/
private function is_common_phrase($phrase) {
$common_phrases = [
‘the quick brown fox’,
‘lorem ipsum dolor’,
‘in the beginning’,
‘once upon a time’,
‘to be or not to be’,
‘it was the best of times’,
‘in conclusion’,
‘first of all’,
‘on the other hand’,
‘as a result’,
‘in other words’,
‘for example’,
‘such as’,
‘in addition’,
‘furthermore’,
‘moreover’,
‘however’,
‘nevertheless’,
‘therefore’,
‘consequently’
];

$phrase_lower = strtolower($phrase);

foreach ($common_phrases as $common) {
if (strpos($phrase_lower, $common) !== false) {
return true;
}
}

// Check for very common word patterns
$common_words = [‘the’, ‘and’, ‘or’, ‘but’, ‘in’, ‘on’, ‘at’, ‘to’, ‘for’, ‘of’, ‘with’, ‘by’];
$words = explode(‘ ‘, $phrase_lower);
$common_count = 0;

foreach ($words as $word) {
if (in_array($word, $common_words)) {
$common_count++;
}
}

// If more than 60% of words are common, consider it too common
return ($common_count / count($words)) > 0.6;
}

/**
* Clean and normalize text for comparison
*/
private function clean_text($text) {
// Remove HTML tags
$text = strip_tags($text);

// Convert to lowercase
$text = strtolower($text);

// Remove extra whitespace and normalize punctuation
$text = preg_replace(‘/[^\w\s]/’, ‘ ‘, $text);
$text = preg_replace(‘/\s+/’, ‘ ‘, $text);

// Remove very short words (1-2 characters)
$words = explode(‘ ‘, $text);
$words = array_filter($words, function($word) {
return strlen($word) > 2;
});

return trim(implode(‘ ‘, $words));
}

/**
* Calculate severity based on match length and type
*/
private function calculate_severity($length, $type = ‘exact’) {
if ($type === ‘exact’) {
if ($length >= 12) return ‘high’;
if ($length >= 8) return ‘medium’;
return ‘low’;
} else {
if ($length >= 15) return ‘high’;
if ($length >= 10) return ‘medium’;
return ‘low’;
}
}

/**
* Remove overlapping matches to avoid duplicates
*/
private function remove_overlapping_matches($matches) {
if (empty($matches)) {
return [];
}

// Sort by position and length
usort($matches, function($a, $b) {
if ($a[‘position’] === $b[‘position’]) {
return $b[‘length’] – $a[‘length’]; // Longer matches first
}
return $a[‘position’] – $b[‘position’];
});

$filtered = [];
$last_end = -1;

foreach ($matches as $match) {
$start = $match[‘position’];
$end = $match[‘position’] + $match[‘length’];

// If this match doesn’t overlap with the previous one, keep it
if ($start >= $last_end) {
$filtered[] = $match;
$last_end = $end;
}
}

return $filtered;
}

/**
* Highlight matches in text for display
*/
public function highlight_matches($text, $matches) {
if (empty($matches)) {
return $text;
}

$highlighted_text = $text;

// Sort matches by phrase length (longest first) to avoid nested highlighting
usort($matches, function($a, $b) {
return strlen($b[‘phrase’]) – strlen($a[‘phrase’]);
});

foreach ($matches as $match) {
$phrase = $match[‘phrase’];
$severity_class = ‘cdm-match-‘ . $match[‘severity’];
$confidence = isset($match[‘confidence’]) ? $match[‘confidence’] : 100;

$highlight_html = ‘<mark class=”cdm-plagiarism-match ‘ . $severity_class . ‘” ‘ .
‘data-severity=”‘ . $match[‘severity’] . ‘” ‘ .
‘data-confidence=”‘ . $confidence . ‘” ‘ .
‘data-type=”‘ . $match[‘type’] . ‘” ‘ .
‘title=”‘ . ucfirst($match[‘severity’]) . ‘ similarity (‘ . $confidence . ‘% confidence)”>’ .
$phrase . ‘</mark>’;

// Use case-insensitive replacement
$highlighted_text = str_ireplace($phrase, $highlight_html, $highlighted_text);
}

return $highlighted_text;
}

/**
* Generate a comprehensive plagiarism report
*/
public function generate_report($matches, $text1_length, $text2_length) {
$total_matches = count($matches);
$severity_counts = [‘high’ => 0, ‘medium’ => 0, ‘low’ => 0];
$total_matched_words = 0;
$exact_matches = 0;
$fuzzy_matches = 0;

foreach ($matches as $match) {
$severity_counts[$match[‘severity’]]++;
$total_matched_words += $match[‘length’];

if ($match[‘type’] === ‘exact’) {
$exact_matches++;
} else {
$fuzzy_matches++;
}
}

// Calculate overall risk level
$risk_level = ‘low’;
if ($severity_counts[‘high’] > 2 || $total_matches > 15) {
$risk_level = ‘high’;
} elseif ($severity_counts[‘high’] > 0 || $severity_counts[‘medium’] > 5 || $total_matches > 8) {
$risk_level = ‘medium’;
}

// Calculate plagiarism percentage
$words_in_text2 = str_word_count(strip_tags($text2_length));
$plagiarism_percentage = $words_in_text2 > 0 ? round(($total_matched_words / $words_in_text2) * 100, 2) : 0;

return [
‘total_matches’ => $total_matches,
‘exact_matches’ => $exact_matches,
‘fuzzy_matches’ => $fuzzy_matches,
‘severity_breakdown’ => $severity_counts,
‘risk_level’ => $risk_level,
‘plagiarism_percentage’ => $plagiarism_percentage,
‘total_matched_words’ => $total_matched_words,
‘recommendation’ => $this->get_recommendation($risk_level, $plagiarism_percentage),
‘confidence_score’ => $this->calculate_overall_confidence($matches)
];
}

/**
* Calculate overall confidence score
*/
private function calculate_overall_confidence($matches) {
if (empty($matches)) {
return 0;
}

$total_confidence = 0;
foreach ($matches as $match) {
$confidence = isset($match[‘confidence’]) ? $match[‘confidence’] : 100;
$total_confidence += $confidence;
}

return round($total_confidence / count($matches), 2);
}

/**
* Get recommendation based on risk level and plagiarism percentage
*/
private function get_recommendation($risk_level, $percentage) {
switch ($risk_level) {
case ‘high’:
return “High plagiarism detected ($percentage% similarity). Extensive revision required before publication.”;
case ‘medium’:
return “Moderate plagiarism detected ($percentage% similarity). Review and revise highlighted sections.”;
default:
return “Low plagiarism detected ($percentage% similarity). Content appears mostly original.”;
}
}

/**
* Save plagiarism results to database
*/
public function save_results($post_id, $matches, $report) {
if (class_exists(‘CDM_Database’)) {
return CDM_Database::save_plagiarism_result(
$post_id,
$matches,
$report[‘confidence_score’],
$report[‘risk_level’]
);
}
return false;
}
}

### extract_transcripts.py
✅ YouTube transcript fetching via Python script

#!/usr/bin/env python3
“””
YouTube Transcript Extractor for Content Draft Manager
Last Update: June 09, 2025 at 03:50 AM GMT – Integrated with CDM
Purpose: Extract transcripts from YouTube videos with language preferences
Dependencies: youtube-transcript-api
“””

import os
import sys
from youtube_transcript_api import YouTubeTranscriptApi
import time
import random
import re

def get_video_id(url):
“””Extract video ID from YouTube URL”””
if “youtube.com/watch?v=” in url:
return url.split(“watch?v=”)[1].split(“&”)[0]
elif “youtu.be/” in url:
return url.split(“youtu.be/”)[1].split(“?”)[0]
return url

def get_transcript_with_retry(video_id, preference, max_retries=5):
“””Get transcript with retry logic for intermittent errors”””
for attempt in range(max_retries):
try:
return get_transcript_by_preference(video_id, preference)
except Exception as e:
error_msg = str(e)

if “no element found: line 1, column 0″ in error_msg:
if attempt < max_retries – 1:
wait_time = 3 + (attempt * 2) + random.uniform(0, 2)
print(f” ⚠️ XML parse error (attempt {attempt + 1}/{max_retries}), retrying in {wait_time:.1f} seconds…”)
time.sleep(wait_time)
continue
else:
return None, f’XML Parse Error after {max_retries} attempts – YouTube API issue’

elif “not well-formed” in error_msg or “ParseError” in error_msg:
if attempt < max_retries – 1:
wait_time = 2 + attempt + random.uniform(0, 1)
print(f” ⚠️ XML format error (attempt {attempt + 1}/{max_retries}), retrying in {wait_time:.1f} seconds…”)
time.sleep(wait_time)
continue
else:
return None, f’XML Format Error after {max_retries} attempts – YouTube API issue’
else:
return None, f’Error: {error_msg}’

return None, ‘Max retries exceeded’

def get_transcript_by_preference(video_id, preference):
“””Get transcript based on user preference”””
try:
transcript_list = YouTubeTranscriptApi.list_transcripts(video_id)

if preference == “en”:
transcript = transcript_list.find_transcript([‘en’])
fetched_transcript = transcript.fetch()
return process_transcript_data(fetched_transcript), ‘English (Original)’

elif preference == “es”:
transcript = transcript_list.find_transcript([‘es’])
fetched_transcript = transcript.fetch()
return process_transcript_data(fetched_transcript), ‘Spanish (Original)’

elif preference == “en-translate”:
try:
transcript = transcript_list.find_transcript([‘en’])
fetched_transcript = transcript.fetch()
return process_transcript_data(fetched_transcript), ‘English (Original)’
except:
for transcript in transcript_list:
if transcript.is_translatable:
translated = transcript.translate(‘en’)
fetched_transcript = translated.fetch()
return process_transcript_data(fetched_transcript), f’English (Translated from {transcript.language})’

elif preference == “es-translate”:
try:
transcript = transcript_list.find_transcript([‘es’])
fetched_transcript = transcript.fetch()
return process_transcript_data(fetched_transcript), ‘Spanish (Original)’
except:
for transcript in transcript_list:
if transcript.is_translatable:
translated = transcript.translate(‘es’)
fetched_transcript = translated.fetch()
return process_transcript_data(fetched_transcript), f’Spanish (Translated from {transcript.language})’

else:
# Auto mode
try:
transcript = transcript_list.find_transcript([‘en’])
fetched_transcript = transcript.fetch()
return process_transcript_data(fetched_transcript), ‘English (Original)’
except:
pass

try:
transcript = transcript_list.find_transcript([‘es’])
fetched_transcript = transcript.fetch()
return process_transcript_data(fetched_transcript), ‘Spanish (Original)’
except:
pass

try:
for transcript in transcript_list:
if transcript.is_translatable:
translated = transcript.translate(‘en’)
fetched_transcript = translated.fetch()
return process_transcript_data(fetched_transcript), f’English (Translated from {transcript.language})’
except:
pass

transcript = list(transcript_list)[0]
fetched_transcript = transcript.fetch()
return process_transcript_data(fetched_transcript), f'{transcript.language} (Original)’

except Exception as e:
raise e

def process_transcript_data(fetched_transcript):
“””Process transcript data to handle both old and new API formats”””
transcript_text_parts = []

for item in fetched_transcript:
if hasattr(item, ‘text’):
transcript_text_parts.append(item.text)
elif isinstance(item, dict) and ‘text’ in item:
transcript_text_parts.append(item[‘text’])
else:
transcript_text_parts.append(str(item))

return ” “.join(transcript_text_parts)

def main():
if len(sys.argv) < 2:
print(“Error: No input file provided.”)
return

input_file = sys.argv[1]
language_preference = sys.argv[2] if len(sys.argv) > 2 else “auto”
output_file = os.path.dirname(input_file) + ‘/all_transcripts.txt’

if not os.path.exists(input_file):
print(f”Error: Input file ‘{input_file}’ not found.”)
return

with open(input_file, “r”) as f:
urls = [line.strip() for line in f if line.strip()]

print(f”Found {len(urls)} YouTube URLs to process with preference: {language_preference}”)

with open(output_file, “w”, encoding=”utf-8″) as out_f:
successful_count = 0

for i, url in enumerate(urls):
try:
video_id = get_video_id(url)
print(f”\nProcessing video {i+1}/{len(urls)}: {video_id}”)

transcript_data, language_info = get_transcript_with_retry(video_id, language_preference)

if transcript_data:
# Remove URL from output – only show video number and language
out_f.write(f”=== VIDEO {i+1}: ({language_info}) ===\n”)
out_f.write(transcript_data)
out_f.write(“\n\n”)

print(f” ✅ Transcript added successfully in {language_info}”)
successful_count += 1
else:
print(f” ❌ Failed: {language_info}”)
out_f.write(f”=== ERROR for VIDEO {i+1}: ===\n{language_info}\n\n”)

except Exception as e:
print(f” ❌ Unexpected error: {str(e)}”)
out_f.write(f”=== ERROR for VIDEO {i+1}: ===\n{str(e)}\n\n”)

print(f”\n🎉 Processing complete! {successful_count}/{len(urls)} transcripts successfully retrieved.”)
print(f”📄 Results saved to: {output_file}”)

if __name__ == “__main__”:
main()

### admin.css
⚠️ Dashboard grid alignment requires CSS fixes

/* Content Draft Manager Admin Styles – SCOPED VERSION */

/* SCOPE ALL STYLES TO PLUGIN PAGES ONLY */
.toplevel_page_draft-manager .cdm-dashboard,
.content-draft-manager_page_draft-manager-settings,
.post-type-content_draft {
/* Dashboard styles only apply to plugin pages */
}

/* Dashboard Styles – PROPERLY SCOPED */
.toplevel_page_draft-manager .cdm-dashboard {
margin-top: 20px;
}

/* Quick Actions Bar – SCOPED TO PLUGIN ONLY */
.toplevel_page_draft-manager .cdm-quick-actions-bar {
margin: 20px 0 30px 0;
padding: 20px;
background: #fff;
border: 1px solid #ddd;
border-radius: 5px;
text-align: center;
}

/* Button styles – ONLY for plugin pages */
.toplevel_page_draft-manager .cdm-quick-actions-bar .button,
.toplevel_page_draft-manager .cdm-quick-actions-bar .button-hero,
.toplevel_page_draft-manager .cdm-quick-actions-bar .button-primary,
.toplevel_page_draft-manager .cdm-quick-actions-bar .button-secondary {
margin: 0 10px 10px 0;
min-width: 160px;
width: 160px;
height: 45px;
padding: 10px 16px;
font-size: 14px;
font-weight: 600;
display: inline-flex;
align-items: center;
justify-content: center;
text-align: center;
white-space: nowrap;
box-sizing: border-box;
line-height: 1.2;
}

/* Stats Grid – SCOPED */
.toplevel_page_draft-manager .cdm-stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}

.toplevel_page_draft-manager .cdm-stat-card {
background: #fff;
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
display: flex;
align-items: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.toplevel_page_draft-manager .cdm-stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}

/* Icon styling – SCOPED */
.toplevel_page_draft-manager .cdm-stat-card .stat-icon {
margin-right: 15px;
padding: 15px;
border-radius: 50%;
font-size: 24px;
position: relative;
transition: transform 0.3s ease;
}

.toplevel_page_draft-manager .cdm-stat-card:nth-child(1) .stat-icon {
transform: rotate(-18deg);
background: #e3f2fd;
color: #1976d2;
}

.toplevel_page_draft-manager .cdm-stat-card:nth-child(2) .stat-icon {
transform: rotate(15deg);
background: #e8f5e8;
color: #388e3c;
}

.toplevel_page_draft-manager .cdm-stat-card:nth-child(3) .stat-icon {
transform: rotate(-10deg);
background: #e1f5fe;
color: #0288d1;
}

.toplevel_page_draft-manager .cdm-stat-card:nth-child(4) .stat-icon {
transform: rotate(20deg);
background: #fff3e0;
color: #f57c00;
}

/* Stats Content Styling – ENHANCED */
.toplevel_page_draft-manager .cdm-stat-card .stat-content {
flex: 1;
}

.toplevel_page_draft-manager .cdm-stat-card .stat-content h3 {
margin: 0 0 5px 0;
font-size: 14px;
font-weight: 600;
color: #333;
}

.toplevel_page_draft-manager .cdm-stat-card .stat-number {
font-size: 32px;
font-weight: bold;
color: #0073aa;
line-height: 1;
margin: 5px 0;
}

.toplevel_page_draft-manager .cdm-stat-card .stat-description {
margin: 0;
font-size: 12px;
color: #666;
}

/* Dashboard Grid Styles */
.toplevel_page_draft-manager .cdm-dashboard-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 20px;
margin-top: 20px;
}

.toplevel_page_draft-manager .cdm-dashboard-section {
background: #fff;
border: 1px solid #ddd;
border-radius: 5px;
padding: 20px;
}

.toplevel_page_draft-manager .cdm-dashboard-section h2 {
margin-top: 0;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
display: flex;
align-items: center;
gap: 8px;
}

/* Draft Items Styling */
.toplevel_page_draft-manager .cdm-drafts-list {
margin-top: 15px;
}

.toplevel_page_draft-manager .cdm-draft-item {
padding: 15px;
border: 1px solid #eee;
border-radius: 5px;
margin-bottom: 10px;
}

.toplevel_page_draft-manager .draft-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}

.toplevel_page_draft-manager .draft-header h4 {
margin: 0;
}

.toplevel_page_draft-manager .draft-status {
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
font-weight: bold;
}

.toplevel_page_draft-manager .status-publish {
background: #46b450;
color: white;
}

.toplevel_page_draft-manager .status-draft {
background: #ffb900;
color: white;
}

.toplevel_page_draft-manager .draft-meta {
display: flex;
gap: 20px;
margin-bottom: 10px;
font-size: 13px;
color: #666;
}

.toplevel_page_draft-manager .draft-progress {
display: flex;
align-items: center;
gap: 10px;
}

.toplevel_page_draft-manager .progress-bar {
flex: 1;
height: 8px;
background: #eee;
border-radius: 4px;
overflow: hidden;
}

.toplevel_page_draft-manager .progress-fill {
height: 100%;
background: #0073aa;
transition: width 0.3s ease;
}

.toplevel_page_draft-manager .progress-text {
font-size: 12px;
color: #666;
}

/* Chart Styling */
.toplevel_page_draft-manager .cdm-simple-chart {
margin-top: 15px;
}

.toplevel_page_draft-manager .chart-item {
margin-bottom: 15px;
}

.toplevel_page_draft-manager .chart-bar {
height: 20px;
background: #eee;
border-radius: 10px;
overflow: hidden;
margin-bottom: 5px;
}

.toplevel_page_draft-manager .chart-fill {
height: 100%;
transition: width 0.3s ease;
}

.toplevel_page_draft-manager .chart-label {
display: flex;
justify-content: space-between;
font-size: 13px;
}

/* System Status */
.toplevel_page_draft-manager .cdm-status-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-top: 15px;
}

.toplevel_page_draft-manager .status-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px;
border: 1px solid #eee;
border-radius: 5px;
}

.toplevel_page_draft-manager .status-indicator {
width: 12px;
height: 12px;
border-radius: 50%;
}

.toplevel_page_draft-manager .status-good .status-indicator {
background: #46b450;
}

.toplevel_page_draft-manager .status-warning .status-indicator {
background: #ffb900;
}

.toplevel_page_draft-manager .cdm-empty-state {
text-align: center;
padding: 40px 20px;
color: #666;
}

.toplevel_page_draft-manager .cdm-empty-state .dashicons {
font-size: 48px;
opacity: 0.5;
}

.toplevel_page_draft-manager .cdm-section-footer {
margin-top: 20px;
text-align: center;
}

.toplevel_page_draft-manager .cdm-status-notice {
margin-top: 15px;
padding: 10px;
background: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 5px;
}

/* DRAFT FORM STYLES – ONLY for content_draft post type */
.post-type-content_draft .cdm-form-container {
max-width: 100%;
margin: 20px 0;
}

.post-type-content_draft .cdm-step-nav {
display: flex;
margin-bottom: 30px;
border-bottom: 2px solid #ddd;
}

.post-type-content_draft .cdm-step {
flex: 1;
text-align: center;
padding: 15px;
cursor: pointer;
border-bottom: 3px solid transparent;
transition: all 0.3s ease;
}

.post-type-content_draft .cdm-step.active {
border-bottom-color: #0073aa;
background: #f0f6fc;
}

.post-type-content_draft .cdm-step:hover {
background: #f9f9f9;
}

.post-type-content_draft .step-number {
display: block;
width: 30px;
height: 30px;
line-height: 30px;
background: #ddd;
border-radius: 50%;
margin: 0 auto 5px;
font-weight: bold;
}

.post-type-content_draft .cdm-step.active .step-number {
background: #0073aa;
color: white;
}

/* Form Steps */
.post-type-content_draft .cdm-form-step {
display: none;
padding: 20px 0;
}

.post-type-content_draft .cdm-form-step.active {
display: block;
}

.post-type-content_draft .cdm-form-row {
margin-bottom: 20px;
}

.post-type-content_draft .cdm-form-row label {
display: block;
font-weight: bold;
margin-bottom: 5px;
}

.post-type-content_draft .cdm-form-row input[type=”text”],
.post-type-content_draft .cdm-form-row input[type=”url”],
.post-type-content_draft .cdm-form-row textarea {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}

.post-type-content_draft .cdm-form-row textarea {
min-height: 120px;
resize: vertical;
}

.post-type-content_draft .cdm-input-group {
display: flex;
gap: 10px;
align-items: flex-start;
}

.post-type-content_draft .cdm-input-group input[type=”url”],
.post-type-content_draft .cdm-input-group textarea {
flex: 1;
}

.post-type-content_draft .cdm-input-group button {
flex-shrink: 0;
}

/* ENHANCED TRAILER CONTROLS WITH LANGUAGE SELECTION */
.post-type-content_draft .cdm-trailer-controls {
display: flex;
gap: 10px;
align-items: center;
flex-shrink: 0;
}

.post-type-content_draft .cdm-language-select {
padding: 6px 8px;
border: 1px solid #ddd;
border-radius: 4px;
background: white;
font-size: 13px;
min-width: 100px;
}

/* IMDB Data Display */
.post-type-content_draft .cdm-imdb-data {
margin-top: 20px;
padding: 20px;
background: #f9f9f9;
border: 1px solid #ddd;
border-radius: 5px;
}

.post-type-content_draft .cdm-imdb-grid {
display: grid;
grid-template-columns: 200px 1fr;
gap: 20px;
margin-top: 15px;
}

.post-type-content_draft .cdm-imdb-poster img {
max-width: 100%;
height: auto;
border-radius: 5px;
}

.post-type-content_draft .cdm-imdb-details h4 {
margin-top: 0;
color: #0073aa;
}

.post-type-content_draft .cdm-imdb-meta {
margin: 10px 0;
line-height: 1.5;
}

/* YouTube Embed Preview – FIXED SIZE */
.post-type-content_draft .cdm-embed-preview {
margin-top: 15px;
padding: 15px;
background: #f0f0f1;
border-radius: 5px;
}

.post-type-content_draft .cdm-youtube-container {
max-width: 560px;
margin: 0 auto;
}

.post-type-content_draft .cdm-youtube-container iframe {
width: 560px;
height: 315px;
max-width: 100%;
}

/* Navigation */
.post-type-content_draft .cdm-navigation {
margin-top: 30px;
text-align: center;
}

.post-type-content_draft .cdm-navigation button {
margin: 0 10px;
}

/* Hide WordPress meta boxes for content_draft post type */
.post-type-content_draft #pageparentdiv,
.post-type-content_draft #authordiv,
.post-type-content_draft #slugdiv,
.post-type-content_draft #postcustom,
.post-type-content_draft #commentstatusdiv,
.post-type-content_draft #commentsdiv,
.post-type-content_draft #trackbacksdiv,
.post-type-content_draft #revisionsdiv,
.post-type-content_draft #formatdiv,
.post-type-content_draft #tagsdiv-post_tag {
display: none;
}

/* Hide WordPress admin footer and notices in CDM forms */
.cdm-form-container #wpfooter,
.cdm-form-container .update-nag,
.cdm-form-container #wp-version-message,
.cdm-form-container .wp-admin-notice,
.cdm-form-container #footer-thankyou {
display: none !important;
}

/* Ensure clean form container */
.cdm-form-container {
position: relative;
background: #fff;
padding: 20px;
border-radius: 5px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

/* Hide any WordPress admin elements that might bleed through */
.cdm-tabs-container .wp-admin-notice,
.cdm-tabs-container #wpfooter {
display: none !important;
}

/* Responsive Design */
@media (max-width: 768px) {
.toplevel_page_draft-manager .cdm-stats-grid {
grid-template-columns: 1fr;
}

.toplevel_page_draft-manager .cdm-dashboard-grid {
grid-template-columns: 1fr;
}

.post-type-content_draft .cdm-imdb-grid {
grid-template-columns: 1fr;
}

.post-type-content_draft .cdm-input-group {
flex-direction: column;
}

.post-type-content_draft .cdm-trailer-controls {
flex-direction: column;
width: 100%;
}

.post-type-content_draft .cdm-language-select {
width: 100%;
}

.post-type-content_draft .cdm-youtube-container iframe {
width: 100%;
height: auto;
aspect-ratio: 16/9;
}
}

/**
* YouTube Transcript Fetching Styles
* Last Update: June 08, 2025 at 09:35 PM GMT – Added transcript UI styles
* Purpose: Styles for transcript fetching interface
*/

/* Transcript Controls */
.cdm-transcript-controls {
padding: 10px;
background: #f8f9fa;
border-radius: 4px;
border: 1px solid #dee2e6;
}

.cdm-language-select {
padding: 4px 8px;
border: 1px solid #ccc;
border-radius: 3px;
font-size: 12px;
}

/* Transcript Loading Indicator */
.cdm-transcript-loading {
text-align: center;
padding: 30px 20px;
background: #f6f7f7;
border-left: 4px solid #72aee6;
margin: 15px 0;
border-radius: 4px;
}

.cdm-transcript-loading .loading-content h4 {
color: #1d2327;
margin: 15px 0 10px 0;
}

.cdm-transcript-loading .loading-content p {
color: #646970;
margin: 0;
}

.cdm-transcript-loading .loading-spinner {
border: 3px solid #f3f4f5;
border-top: 3px solid #2271b1;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin: 0 auto 20px auto;
}

/* Fetch Transcripts Button */
#cdm-fetch-transcripts {
margin-left: 10px;
vertical-align: top;
}

#cdm-fetch-transcripts.processing {
opacity: 0.7;
cursor: not-allowed;
}

#cdm-fetch-transcripts .dashicons {
font-size: 16px;
line-height: 1;
vertical-align: middle;
}

/* Sources textarea enhancement */
#cdm_sources {
font-family: Consolas, Monaco, monospace;
font-size: 13px;
line-height: 1.4;
}

/* Responsive design */
@media (max-width: 768px) {
#cdm-fetch-transcripts {
margin-left: 0;
margin-top: 8px;
display: block;
width: 100%;
}

.cdm-transcript-controls {
margin-top: 10px;
}
}

/* YouTube Transcript Fetching Styles – Ends Here */

### dashboard.php
✅ Admin dashboard template complete

<?php
/**
* Content Draft Manager Dashboard
* Admin dashboard template for the plugin
*/

// Prevent direct access
if (!defined(‘ABSPATH’)) {
exit;
}

// Get basic statistics (simplified to avoid database errors)
global $wpdb;

// Count total drafts
$total_drafts = wp_count_posts(‘content_draft’);
$total_drafts_count = $total_drafts->publish + $total_drafts->draft + $total_drafts->private;

// Get recent drafts
$recent_drafts = get_posts([
‘post_type’ => ‘content_draft’,
‘posts_per_page’ => 5,
‘orderby’ => ‘modified’,
‘order’ => ‘DESC’,
‘post_status’ => [‘publish’, ‘draft’, ‘private’]
]);

// Simple activity count (last 7 days)
$recent_activity_count = count(get_posts([
‘post_type’ => ‘content_draft’,
‘posts_per_page’ => -1,
‘date_query’ => [
‘after’ => ‘1 week ago’
]
]));

// Get plagiarism checks count (with error handling)
$plagiarism_table = $wpdb->prefix . ‘cdm_plagiarism_results’;
$plagiarism_checks_today = 0;
$high_risk_drafts = 0;

if ($wpdb->get_var(“SHOW TABLES LIKE ‘$plagiarism_table’”) == $plagiarism_table) {
$plagiarism_checks_today = $wpdb->get_var(
“SELECT COUNT(*) FROM $plagiarism_table WHERE DATE(scan_date) = CURDATE()”
) ?: 0;

$high_risk_drafts = $wpdb->get_var(
“SELECT COUNT(DISTINCT post_id) FROM $plagiarism_table WHERE risk_level = ‘high’ AND scan_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)”
) ?: 0;
}

// Get category statistics
$category_stats = [];
$terms = get_terms([
‘taxonomy’ => ‘category’,
‘hide_empty’ => false,
]);

foreach ($terms as $term) {
$count = $wpdb->get_var($wpdb->prepare(
“SELECT COUNT(*) FROM {$wpdb->posts} p
INNER JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
WHERE p.post_type = ‘content_draft’ AND tr.term_taxonomy_id = %d”,
$term->term_taxonomy_id
));

if ($count > 0) {
$category_stats[] = [
‘name’ => $term->name,
‘count’ => $count,
‘color’ => sprintf(‘#%06X’, mt_rand(0, 0xFFFFFF))
];
}
}
?>

<div class=”wrap cdm-dashboard”>
<h1 class=”wp-heading-inline”>
<span class=”dashicons dashicons-edit-page”></span>
Draft Manager Dashboard
</h1>

<!– Quick Actions Bar – FIXED WITHOUT ICONS –>
<div class=”cdm-quick-actions-bar”>
<a href=”<?php echo admin_url(‘post-new.php?post_type=content_draft’); ?>”
class=”button button-primary button-hero”>
Create New Draft
</a>

<a href=”<?php echo admin_url(‘edit.php?post_type=content_draft’); ?>”
class=”button button-secondary button-hero”>
View All Drafts
</a>

<a href=”<?php echo admin_url(‘admin.php?page=draft-manager-settings’); ?>”
class=”button button-secondary button-hero”>
Settings
</a>
</div>

<!– Statistics Cards – FIXED WITH PROPER CLASSES –>
<div class=”cdm-stats-grid”>
<div class=”cdm-stat-card”>
<div class=”stat-icon”>
<span class=”dashicons dashicons-edit-page”></span>
</div>
<div class=”stat-content”>
<h3>Total Drafts</h3>
<div class=”stat-number”><?php echo number_format($total_drafts_count); ?></div>
<p class=”stat-description”>All content drafts</p>
</div>
</div>

<div class=”cdm-stat-card”>
<div class=”stat-icon”>
<span class=”dashicons dashicons-chart-line”></span>
</div>
<div class=”stat-content”>
<h3>Recent Activity</h3>
<div class=”stat-number”><?php echo number_format($recent_activity_count); ?></div>
<p class=”stat-description”>Actions in last 7 days</p>
</div>
</div>

<div class=”cdm-stat-card”>
<div class=”stat-icon”>
<span class=”dashicons dashicons-search”></span>
</div>
<div class=”stat-content”>
<h3>Plagiarism Checks</h3>
<div class=”stat-number”><?php echo number_format($plagiarism_checks_today); ?></div>
<p class=”stat-description”>Scans performed today</p>
</div>
</div>

<div class=”cdm-stat-card”>
<div class=”stat-icon”>
<span class=”dashicons dashicons-warning”></span>
</div>
<div class=”stat-content”>
<h3>High Risk Drafts</h3>
<div class=”stat-number”><?php echo number_format($high_risk_drafts); ?></div>
<p class=”stat-description”>Need attention this month</p>
</div>
</div>
</div>

<!– Main Content Grid –>
<div class=”cdm-dashboard-grid”>

<!– Recent Drafts Section –>
<div class=”cdm-dashboard-section”>
<h2>
<span class=”dashicons dashicons-clock”></span>
Recent Drafts
</h2>

<?php if (!empty($recent_drafts)): ?>
<div class=”cdm-drafts-list”>
<?php foreach ($recent_drafts as $draft): ?>
<?php
$draft_status = get_post_status($draft->ID);
$last_modified = human_time_diff(strtotime($draft->post_modified), current_time(‘timestamp’)) . ‘ ago’;
$author = get_userdata($draft->post_author);

// Get completion percentage
$sections = [
‘cdm_imdb_url’, ‘cdm_youtube_embed’, ‘cdm_sources’,
‘cdm_research_data’, ‘cdm_prompt’, ‘cdm_ai_article’, ‘cdm_final_edit’
];
$completed = 0;
foreach ($sections as $section) {
if (!empty(get_post_meta($draft->ID, $section, true))) {
$completed++;
}
}
$completion_percentage = round(($completed / count($sections)) * 100);
?>

<div class=”cdm-draft-item”>
<div class=”draft-header”>
<h4>
<a href=”<?php echo get_edit_post_link($draft->ID); ?>”>
<?php echo esc_html($draft->post_title ?: ‘Untitled Draft’); ?>
</a>
</h4>
<span class=”draft-status status-<?php echo $draft_status; ?>”>
<?php echo ucfirst($draft_status); ?>
</span>
</div>

<div class=”draft-meta”>
<span class=”draft-author”>
<span class=”dashicons dashicons-admin-users”></span>
<?php echo esc_html($author->display_name); ?>
</span>
<span class=”draft-modified”>
<span class=”dashicons dashicons-clock”></span>
<?php echo $last_modified; ?>
</span>
</div>

<div class=”draft-progress”>
<div class=”progress-bar”>
<div class=”progress-fill” style=”width: <?php echo $completion_percentage; ?>%”></div>
</div>
<span class=”progress-text”><?php echo $completion_percentage; ?>% complete</span>
</div>
</div>
<?php endforeach; ?>
</div>

<div class=”cdm-section-footer”>
<a href=”<?php echo admin_url(‘edit.php?post_type=content_draft’); ?>” class=”button”>
View All Drafts
</a>
</div>
<?php else: ?>
<div class=”cdm-empty-state”>
<span class=”dashicons dashicons-edit-page”></span>
<h3>No drafts yet</h3>
<p>Create your first content draft to get started.</p>
<a href=”<?php echo admin_url(‘post-new.php?post_type=content_draft’); ?>”
class=”button button-primary”>
Create First Draft
</a>
</div>
<?php endif; ?>
</div>

<!– Categories Chart –>
<?php if (!empty($category_stats)): ?>
<div class=”cdm-dashboard-section”>
<h2>
<span class=”dashicons dashicons-chart-pie”></span>
Drafts by Category
</h2>

<div class=”cdm-simple-chart”>
<?php foreach ($category_stats as $category): ?>
<?php
$total_categories = array_sum(array_column($category_stats, ‘count’));
$percentage = round(($category[‘count’] / $total_categories) * 100, 1);
?>
<div class=”chart-item”>
<div class=”chart-bar”>
<div class=”chart-fill”
style=”width: <?php echo $percentage; ?>%; background-color: <?php echo $category[‘color’]; ?>”></div>
</div>
<div class=”chart-label”>
<span class=”chart-name”><?php echo esc_html($category[‘name’]); ?></span>
<span class=”chart-count”><?php echo $category[‘count’]; ?> (<?php echo $percentage; ?>%)</span>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>

</div>

<!– System Status –>
<div class=”cdm-dashboard-section”>
<h2>
<span class=”dashicons dashicons-admin-tools”></span>
System Status
</h2>

<div class=”cdm-status-grid”>
<div class=”status-item status-<?php echo get_option(‘cdm_youtube_api_key’) ? ‘good’ : ‘warning’; ?>”>
<span class=”status-indicator”></span>
<span class=”status-label”>YouTube API</span>
<span class=”status-value”>
<?php echo get_option(‘cdm_youtube_api_key’) ? ‘Configured’ : ‘Not Set’; ?>
</span>
</div>

<div class=”status-item status-<?php echo get_option(‘cdm_groq_api_key’) ? ‘good’ : ‘warning’; ?>”>
<span class=”status-indicator”></span>
<span class=”status-label”>Groq API</span>
<span class=”status-value”>
<?php echo get_option(‘cdm_groq_api_key’) ? ‘Configured’ : ‘Not Set’; ?>
</span>
</div>

<div class=”status-item status-good”>
<span class=”status-indicator”></span>
<span class=”status-label”>Database</span>
<span class=”status-value”>Connected</span>
</div>

<div class=”status-item status-good”>
<span class=”status-indicator”></span>
<span class=”status-label”>Auto-save</span>
<span class=”status-value”>
<?php echo get_option(‘cdm_auto_save_interval’, 30); ?>s interval
</span>
</div>
</div>

<?php if (!get_option(‘cdm_youtube_api_key’) || !get_option(‘cdm_groq_api_key’)): ?>
<div class=”cdm-status-notice”>
<p>
<span class=”dashicons dashicons-info”></span>
Some features require API configuration.
<a href=”<?php echo admin_url(‘admin.php?page=draft-manager-settings’); ?>”>
Configure settings
</a>
</p>
</div>
<?php endif; ?>
</div>

</div>

### settings.php
✅ API key management and configuration complete

<?php
/**
* Content Draft Manager Settings Page – Simple Version
* All settings on one page without tabs
*/

// Prevent direct access
if (!defined(‘ABSPATH’)) {
exit;
}

// Handle form submission
if (isset($_POST[‘submit’])) {
// Verify nonce
if (!wp_verify_nonce($_POST[‘cdm_settings_nonce’], ‘cdm_save_settings’)) {
wp_die(‘Security check failed’);
}

// Check user permissions
if (!current_user_can(‘manage_options’)) {
wp_die(‘You do not have sufficient permissions to access this page.’);
}

// Save all settings
update_option(‘cdm_youtube_api_key’, sanitize_text_field($_POST[‘youtube_api_key’]));
update_option(‘cdm_groq_api_key’, sanitize_text_field($_POST[‘groq_api_key’]));
update_option(‘cdm_tmdb_api_key’, sanitize_text_field($_POST[‘tmdb_api_key’]));
update_option(‘cdm_streaming_api_key’, sanitize_text_field($_POST[‘streaming_api_key’]));
update_option(‘cdm_claude_api_key’, sanitize_text_field($_POST[‘cdm_claude_api_key’]));
update_option(‘cdm_auto_save_interval’, intval($_POST[‘auto_save_interval’]));
update_option(‘cdm_plagiarism_min_words’, intval($_POST[‘plagiarism_min_words’]));
update_option(‘cdm_cache_duration’, intval($_POST[‘cache_duration’]));
update_option(‘cdm_max_plagiarism_history’, intval($_POST[‘max_plagiarism_history’]));
update_option(‘cdm_activity_log_retention’, intval($_POST[‘activity_log_retention’]));
update_option(‘cdm_email_notifications’, isset($_POST[’email_notifications’]) ? 1 : 0);
update_option(‘cdm_notification_email’, sanitize_email($_POST[‘notification_email’]));
update_option(‘cdm_notify_high_plagiarism’, isset($_POST[‘notify_high_plagiarism’]) ? 1 : 0);
update_option(‘cdm_notify_draft_completion’, isset($_POST[‘notify_draft_completion’]) ? 1 : 0);
update_option(‘cdm_enable_debug_mode’, isset($_POST[‘enable_debug_mode’]) ? 1 : 0);
update_option(‘cdm_cleanup_on_deactivation’, isset($_POST[‘cleanup_on_deactivation’]) ? 1 : 0);
update_option(‘cdm_enable_activity_logging’, isset($_POST[‘enable_activity_logging’]) ? 1 : 0);

echo ‘<div class=”notice notice-success is-dismissible”><p><strong>Settings saved successfully!</strong></p></div>’;
}

// Handle cleanup action
if (isset($_POST[‘cleanup_data’])) {
if (!wp_verify_nonce($_POST[‘cdm_cleanup_nonce’], ‘cdm_cleanup_data’)) {
wp_die(‘Security check failed’);
}
CDM_Database::cleanup_expired_data();
echo ‘<div class=”notice notice-success is-dismissible”><p><strong>Expired data cleaned up successfully!</strong></p></div>’;
}

// Get current settings
$youtube_api_key = get_option(‘cdm_youtube_api_key’, ”);
$groq_api_key = get_option(‘cdm_groq_api_key’, ”);
$tmdb_api_key = get_option(‘cdm_tmdb_api_key’, ”);
$streaming_api_key = get_option(‘cdm_streaming_api_key’, ”);
$claude_api_key = get_option(‘cdm_claude_api_key’, ”);
$auto_save_interval = get_option(‘cdm_auto_save_interval’, 30);
$plagiarism_min_words = get_option(‘cdm_plagiarism_min_words’, 5);
$cache_duration = get_option(‘cdm_cache_duration’, 86400);
$max_plagiarism_history = get_option(‘cdm_max_plagiarism_history’, 50);
$activity_log_retention = get_option(‘cdm_activity_log_retention’, 2592000);
$email_notifications = get_option(‘cdm_email_notifications’, 0);
$notification_email = get_option(‘cdm_notification_email’, get_option(‘admin_email’));
$notify_high_plagiarism = get_option(‘cdm_notify_high_plagiarism’, 0);
$notify_draft_completion = get_option(‘cdm_notify_draft_completion’, 0);
$enable_debug_mode = get_option(‘cdm_enable_debug_mode’, 0);
$cleanup_on_deactivation = get_option(‘cdm_cleanup_on_deactivation’, 0);
$enable_activity_logging = get_option(‘cdm_enable_activity_logging’, 1);

// Get system info
global $wpdb;
$plugin_version = CDM_VERSION;
$wp_version = get_bloginfo(‘version’);
?>

<div class=”wrap”>
<h1><span class=”dashicons dashicons-admin-settings”></span> Draft Manager Settings</h1>
<p>Configure your Content Draft Manager plugin settings and API integrations.</p>

<form method=”post” action=””>
<?php wp_nonce_field(‘cdm_save_settings’, ‘cdm_settings_nonce’); ?>

<!– API Keys Section –>
<div class=”postbox”>
<h2 class=”hndle”>API Keys</h2>
<div class=”inside”>
<table class=”form-table”>
<tr>
<th><label for=”youtube_api_key”>YouTube API Key</label></th>
<td>
<input type=”text” id=”youtube_api_key” name=”youtube_api_key”
value=”<?php echo esc_attr($youtube_api_key); ?>” class=”regular-text”
placeholder=”AIza…” />
<p class=”description”>Required for automatic trailer fetching from YouTube.</p>
</td>
</tr>
<tr>
<th><label for=”groq_api_key”>Groq API Key</label></th>
<td>
<input type=”text” id=”groq_api_key” name=”groq_api_key”
value=”<?php echo esc_attr($groq_api_key); ?>” class=”regular-text”
placeholder=”gsk_…” />
<p class=”description”>Required for AI content detection features.</p>
</td>
</tr>
<tr>
<th><label for=”tmdb_api_key”>TMDB API Key</label></th>
<td>
<input type=”text” id=”tmdb_api_key” name=”tmdb_api_key”
value=”<?php echo esc_attr($tmdb_api_key); ?>” class=”regular-text”
placeholder=”eyJhbGciOiJIUzI1NiJ9…” />
<p class=”description”>Required for enhanced cast, crew, and reviews data from The Movie Database.</p>
<p class=”description”><strong>Get your free API key:</strong> <a href=”https://www.themoviedb.org/settings/api” target=”_blank”>TMDB API Settings</a></p>
</td>
</tr>
<tr>
<th><label for=”streaming_api_key”>Streaming Availability API Key</label></th>
<td>
<input type=”text” id=”streaming_api_key” name=”streaming_api_key” value=”<?php echo esc_attr($streaming_api_key); ?>” class=”regular-text” placeholder=”your-rapidapi-key-here…” />
<p class=”description”><strong>Get your free API key:</strong> <a href=”https://rapidapi.com/movie-of-the-night-movie-of-the-night-default/api/streaming-availability” target=”_blank”>Streaming Availability API</a></p>
<p class=”description”>Required for automatic streaming platform URL fetching (Netflix, Amazon Prime, etc.)</p>
</td>
</tr>
<tr>
<th scope=”row”>Claude API Key</th>
<td>
<input type=”password” id=”claude_api_key” name=”cdm_claude_api_key”
value=”<?php echo esc_attr($claude_api_key); ?>”
class=”regular-text” />

<p class=”description”>Your Claude API key from Anthropic Console</p>
</td>
</tr>

</table>
</div>
</div>

<!– General Settings Section –>
<div class=”postbox”>
<h2 class=”hndle”>General Settings</h2>
<div class=”inside”>
<table class=”form-table”>
<tr>
<th><label for=”auto_save_interval”>Auto-save Interval</label></th>
<td>
<input type=”number” id=”auto_save_interval” name=”auto_save_interval”
value=”<?php echo esc_attr($auto_save_interval); ?>”
min=”10″ max=”300″ class=”small-text” />
<span class=”description”>seconds (10-300)</span>
</td>
</tr>
<tr>
<th><label for=”plagiarism_min_words”>Plagiarism Detection Sensitivity</label></th>
<td>
<input type=”number” id=”plagiarism_min_words” name=”plagiarism_min_words”
value=”<?php echo esc_attr($plagiarism_min_words); ?>”
min=”3″ max=”15″ class=”small-text” />
<span class=”description”>minimum consecutive words</span>
</td>
</tr>
<tr>
<th><label for=”cache_duration”>Cache Duration</label></th>
<td>
<select id=”cache_duration” name=”cache_duration”>
<option value=”3600″ <?php selected($cache_duration, 3600); ?>>1 Hour</option>
<option value=”21600″ <?php selected($cache_duration, 21600); ?>>6 Hours</option>
<option value=”43200″ <?php selected($cache_duration, 43200); ?>>12 Hours</option>
<option value=”86400″ <?php selected($cache_duration, 86400); ?>>24 Hours</option>
<option value=”259200″ <?php selected($cache_duration, 259200); ?>>3 Days</option>
<option value=”604800″ <?php selected($cache_duration, 604800); ?>>1 Week</option>
</select>
</td>
</tr>
<tr>
<th><label for=”max_plagiarism_history”>Plagiarism History Limit</label></th>
<td>
<input type=”number” id=”max_plagiarism_history” name=”max_plagiarism_history”
value=”<?php echo esc_attr($max_plagiarism_history); ?>”
min=”10″ max=”200″ class=”small-text” />
<span class=”description”>records per draft</span>
</td>
</tr>
<tr>
<th><label for=”activity_log_retention”>Activity Log Retention</label></th>
<td>
<select id=”activity_log_retention” name=”activity_log_retention”>
<option value=”604800″ <?php selected($activity_log_retention, 604800); ?>>1 Week</option>
<option value=”1209600″ <?php selected($activity_log_retention, 1209600); ?>>2 Weeks</option>
<option value=”2592000″ <?php selected($activity_log_retention, 2592000); ?>>1 Month</option>
<option value=”7776000″ <?php selected($activity_log_retention, 7776000); ?>>3 Months</option>
<option value=”15552000″ <?php selected($activity_log_retention, 15552000); ?>>6 Months</option>
<option value=”31536000″ <?php selected($activity_log_retention, 31536000); ?>>1 Year</option>
</select>
</td>
</tr>
</table>
</div>
</div>

<!– Notifications Section –>
<div class=”postbox”>
<h2 class=”hndle”>Email Notifications</h2>
<div class=”inside”>
<table class=”form-table”>
<tr>
<th>Enable Notifications</th>
<td>
<label>
<input type=”checkbox” name=”email_notifications” value=”1″
<?php checked($email_notifications, 1); ?> />
Send email notifications for important events
</label>
</td>
</tr>
<tr>
<th><label for=”notification_email”>Notification Email</label></th>
<td>
<input type=”email” id=”notification_email” name=”notification_email”
value=”<?php echo esc_attr($notification_email); ?>” class=”regular-text” />
</td>
</tr>
<tr>
<th>Notification Types</th>
<td>
<label>
<input type=”checkbox” name=”notify_high_plagiarism” value=”1″
<?php checked($notify_high_plagiarism, 1); ?> />
High plagiarism risk detected
</label><br><br>
<label>
<input type=”checkbox” name=”notify_draft_completion” value=”1″
<?php checked($notify_draft_completion, 1); ?> />
Draft marked as complete
</label>
</td>
</tr>
</table>
</div>
</div>

<!– Advanced Settings Section –>
<div class=”postbox”>
<h2 class=”hndle”>Advanced Settings</h2>
<div class=”inside”>
<table class=”form-table”>
<tr>
<th>Debug Mode</th>
<td>
<label>
<input type=”checkbox” name=”enable_debug_mode” value=”1″
<?php checked($enable_debug_mode, 1); ?> />
Enable debug logging
</label>
</td>
</tr>
<tr>
<th>Activity Logging</th>
<td>
<label>
<input type=”checkbox” name=”enable_activity_logging” value=”1″
<?php checked($enable_activity_logging, 1); ?> />
Track user activity
</label>
</td>
</tr>
<tr>
<th>Data Cleanup</th>
<td>
<label>
<input type=”checkbox” name=”cleanup_on_deactivation” value=”1″
<?php checked($cleanup_on_deactivation, 1); ?> />
Remove all plugin data when deactivated
</label>
<p class=”description”>
<strong>Warning:</strong> This will permanently delete all drafts and settings.
</p>
</td>
</tr>
</table>
</div>
</div>

<!– System Info Section –>
<div class=”postbox”>
<h2 class=”hndle”>System Information</h2>
<div class=”inside”>
<table class=”widefat striped”>
<tr><td><strong>Plugin Version</strong></td><td><?php echo esc_html($plugin_version); ?></td></tr>
<tr><td><strong>WordPress Version</strong></td><td><?php echo esc_html($wp_version); ?></td></tr>
<tr><td><strong>PHP Version</strong></td><td><?php echo esc_html(PHP_VERSION); ?></td></tr>
</table>
</div>
</div>

<?php submit_button(‘Save All Settings’, ‘primary’, ‘submit’); ?>
</form>

<!– Manual Cleanup –>
<div class=”postbox”>
<h2 class=”hndle”>Manual Data Cleanup</h2>
<div class=”inside”>
<p>Remove expired cache data and old activity logs manually.</p>
<form method=”post” style=”display: inline;”>
<?php wp_nonce_field(‘cdm_cleanup_data’, ‘cdm_cleanup_nonce’); ?>
<input type=”submit” name=”cleanup_data” class=”button button-secondary”
value=”Clean Up Expired Data”
onclick=”return confirm(‘Are you sure you want to clean up expired data?’);” />
</form>
</div>
</div>
</div>

<style>
.postbox {
margin-bottom: 20px;
}
.postbox .hndle {
padding: 12px;
font-size: 14px;
font-weight: 600;
background: #f1f1f1;
border-bottom: 1px solid #ddd;
}
.postbox .inside {
padding: 20px;
}
</style>

### class-database.php
✅ Custom table management operational

<?php

class CDM_Database {

public static function create_tables() {
global $wpdb;

$charset_collate = $wpdb->get_charset_collate();

// Draft sections table for storing individual form sections
$table_name = $wpdb->prefix . ‘cdm_draft_sections’;

$sql = “CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
post_id bigint(20) NOT NULL,
section_name varchar(50) NOT NULL,
content longtext,
last_modified datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
modified_by bigint(20),
version int(11) DEFAULT 1,
PRIMARY KEY (id),
KEY post_id (post_id),
KEY section_name (section_name),
KEY last_modified (last_modified),
UNIQUE KEY unique_post_section (post_id, section_name)
) $charset_collate;”;

// Plagiarism results table for storing scan history
$table_name2 = $wpdb->prefix . ‘cdm_plagiarism_results’;

$sql2 = “CREATE TABLE $table_name2 (
id mediumint(9) NOT NULL AUTO_INCREMENT,
post_id bigint(20) NOT NULL,
matches_found int(11) DEFAULT 0,
confidence_score decimal(5,2),
risk_level enum(‘low’,’medium’,’high’) DEFAULT ‘low’,
scan_date datetime DEFAULT CURRENT_TIMESTAMP,
scan_results longtext,
scanned_by bigint(20),
PRIMARY KEY (id),
KEY post_id (post_id),
KEY scan_date (scan_date),
KEY risk_level (risk_level)
) $charset_collate;”;

// IMDB cache table for storing fetched movie data
$table_name3 = $wpdb->prefix . ‘cdm_imdb_cache’;

$sql3 = “CREATE TABLE $table_name3 (
id mediumint(9) NOT NULL AUTO_INCREMENT,
imdb_id varchar(20) NOT NULL,
movie_data longtext,
cached_date datetime DEFAULT CURRENT_TIMESTAMP,
expires_date datetime,
fetch_count int(11) DEFAULT 1,
PRIMARY KEY (id),
UNIQUE KEY imdb_id (imdb_id),
KEY cached_date (cached_date),
KEY expires_date (expires_date)
) $charset_collate;”;

// Activity log table for tracking user actions
$table_name4 = $wpdb->prefix . ‘cdm_activity_log’;

$sql4 = “CREATE TABLE $table_name4 (
id mediumint(9) NOT NULL AUTO_INCREMENT,
user_id bigint(20) NOT NULL,
post_id bigint(20),
action varchar(50) NOT NULL,
section varchar(50),
details longtext,
ip_address varchar(45),
user_agent text,
created_date datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY user_id (user_id),
KEY post_id (post_id),
KEY action (action),
KEY created_date (created_date)
) $charset_collate;”;

require_once(ABSPATH . ‘wp-admin/includes/upgrade.php’);
dbDelta($sql);
dbDelta($sql2);
dbDelta($sql3);
dbDelta($sql4);

// Create default options
self::create_default_options();
}

private static function create_default_options() {
add_option(‘cdm_version’, CDM_VERSION);
add_option(‘cdm_auto_save_interval’, 30);
add_option(‘cdm_plagiarism_min_words’, 5);
add_option(‘cdm_youtube_api_key’, ”);
add_option(‘cdm_groq_api_key’, ”);
add_option(‘cdm_cache_duration’, 86400); // 24 hours
add_option(‘cdm_max_plagiarism_history’, 50);
add_option(‘cdm_activity_log_retention’, 2592000); // 30 days
}

public static function save_draft_section($post_id, $section_name, $content, $user_id = null) {
global $wpdb;

$table_name = $wpdb->prefix . ‘cdm_draft_sections’;
$user_id = $user_id ?: get_current_user_id();

$result = $wpdb->replace(
$table_name,
[
‘post_id’ => $post_id,
‘section_name’ => $section_name,
‘content’ => $content,
‘modified_by’ => $user_id,
‘last_modified’ => current_time(‘mysql’)
],
[‘%d’, ‘%s’, ‘%s’, ‘%d’, ‘%s’]
);

if ($result !== false) {
self::log_activity($user_id, $post_id, ‘section_updated’, $section_name);
}

return $result;
}

public static function get_draft_section($post_id, $section_name) {
global $wpdb;

$table_name = $wpdb->prefix . ‘cdm_draft_sections’;

return $wpdb->get_row(
$wpdb->prepare(
“SELECT * FROM $table_name WHERE post_id = %d AND section_name = %s”,
$post_id,
$section_name
)
);
}

public static function save_plagiarism_result($post_id, $matches, $confidence_score, $risk_level = ‘low’) {
global $wpdb;

$table_name = $wpdb->prefix . ‘cdm_plagiarism_results’;
$user_id = get_current_user_id();

$result = $wpdb->insert(
$table_name,
[
‘post_id’ => $post_id,
‘matches_found’ => count($matches),
‘confidence_score’ => $confidence_score,
‘risk_level’ => $risk_level,
‘scan_results’ => json_encode($matches),
‘scanned_by’ => $user_id,
‘scan_date’ => current_time(‘mysql’)
],
[‘%d’, ‘%d’, ‘%f’, ‘%s’, ‘%s’, ‘%d’, ‘%s’]
);

if ($result) {
self::log_activity($user_id, $post_id, ‘plagiarism_check’, null, [
‘matches_found’ => count($matches),
‘risk_level’ => $risk_level
]);

// Clean up old results
self::cleanup_plagiarism_history($post_id);
}

return $result;
}

public static function get_plagiarism_history($post_id, $limit = 10) {
global $wpdb;

$table_name = $wpdb->prefix . ‘cdm_plagiarism_results’;

return $wpdb->get_results(
$wpdb->prepare(
“SELECT pr.*, u.display_name as scanned_by_name
FROM $table_name pr
LEFT JOIN {$wpdb->users} u ON pr.scanned_by = u.ID
WHERE pr.post_id = %d
ORDER BY pr.scan_date DESC
LIMIT %d”,
$post_id,
$limit
)
);
}

private static function cleanup_plagiarism_history($post_id) {
global $wpdb;

$table_name = $wpdb->prefix . ‘cdm_plagiarism_results’;
$max_history = get_option(‘cdm_max_plagiarism_history’, 50);

$wpdb->query(
$wpdb->prepare(
“DELETE FROM $table_name
WHERE post_id = %d
AND id NOT IN (
SELECT id FROM (
SELECT id FROM $table_name
WHERE post_id = %d
ORDER BY scan_date DESC
LIMIT %d
) as keep_records
)”,
$post_id,
$post_id,
$max_history
)
);
}

public static function cache_imdb_data($imdb_id, $data, $cache_duration = null) {
global $wpdb;

$table_name = $wpdb->prefix . ‘cdm_imdb_cache’;
$cache_duration = $cache_duration ?: get_option(‘cdm_cache_duration’, 86400);
$expires_date = date(‘Y-m-d H:i:s’, time() + $cache_duration);

return $wpdb->replace(
$table_name,
[
‘imdb_id’ => $imdb_id,
‘movie_data’ => json_encode($data),
‘cached_date’ => current_time(‘mysql’),
‘expires_date’ => $expires_date,
‘fetch_count’ => 1
],
[‘%s’, ‘%s’, ‘%s’, ‘%s’, ‘%d’]
);
}

public static function get_cached_imdb_data($imdb_id) {
global $wpdb;

$table_name = $wpdb->prefix . ‘cdm_imdb_cache’;

$result = $wpdb->get_row(
$wpdb->prepare(
“SELECT * FROM $table_name
WHERE imdb_id = %s
AND expires_date > NOW()”,
$imdb_id
)
);

if ($result) {
// Update fetch count
$wpdb->update(
$table_name,
[‘fetch_count’ => $result->fetch_count + 1],
[‘id’ => $result->id],
[‘%d’],
[‘%d’]
);

return json_decode($result->movie_data, true);
}

return false;
}

public static function log_activity($user_id, $post_id, $action, $section = null, $details = null) {
global $wpdb;

$table_name = $wpdb->prefix . ‘cdm_activity_log’;

return $wpdb->insert(
$table_name,
[
‘user_id’ => $user_id,
‘post_id’ => $post_id,
‘action’ => $action,
‘section’ => $section,
‘details’ => $details ? json_encode($details) : null,
‘ip_address’ => self::get_user_ip(),
‘user_agent’ => substr($_SERVER[‘HTTP_USER_AGENT’] ?? ”, 0, 500),
‘created_date’ => current_time(‘mysql’)
],
[‘%d’, ‘%d’, ‘%s’, ‘%s’, ‘%s’, ‘%s’, ‘%s’, ‘%s’]
);
}

public static function get_activity_log($post_id = null, $user_id = null, $limit = 50) {
global $wpdb;

$table_name = $wpdb->prefix . ‘cdm_activity_log’;
$where_conditions = [];
$where_values = [];

if ($post_id) {
$where_conditions[] = ‘al.post_id = %d’;
$where_values[] = $post_id;
}

if ($user_id) {
$where_conditions[] = ‘al.user_id = %d’;
$where_values[] = $user_id;
}

$where_clause = !empty($where_conditions) ? ‘WHERE ‘ . implode(‘ AND ‘, $where_conditions) : ”;
$where_values[] = $limit;

return $wpdb->get_results(
$wpdb->prepare(
“SELECT al.*, u.display_name as user_name, p.post_title
FROM $table_name al
LEFT JOIN {$wpdb->users} u ON al.user_id = u.ID
LEFT JOIN {$wpdb->posts} p ON al.post_id = p.ID
$where_clause
ORDER BY al.created_date DESC
LIMIT %d”,
…$where_values
)
);
}

public static function cleanup_expired_data() {
global $wpdb;

// Clean expired IMDB cache
$imdb_table = $wpdb->prefix . ‘cdm_imdb_cache’;
$wpdb->query(“DELETE FROM $imdb_table WHERE expires_date < NOW()”);

// Clean old activity logs
$activity_table = $wpdb->prefix . ‘cdm_activity_log’;
$retention_days = get_option(‘cdm_activity_log_retention’, 2592000); // 30 days
$cutoff_date = date(‘Y-m-d H:i:s’, time() – $retention_days);

$wpdb->query(
$wpdb->prepare(
“DELETE FROM $activity_table WHERE created_date < %s”,
$cutoff_date
)
);
}

public static function get_dashboard_stats() {
global $wpdb;

$stats = [];

// Total drafts
$stats[‘total_drafts’] = wp_count_posts(‘content_draft’)->publish + wp_count_posts(‘content_draft’)->draft;

// Recent activity (last 7 days)
$activity_table = $wpdb->prefix . ‘cdm_activity_log’;
$stats[‘recent_activity’] = $wpdb->get_var(
“SELECT COUNT(*) FROM $activity_table
WHERE created_date >= DATE_SUB(NOW(), INTERVAL 7 DAY)”
);

// Plagiarism checks today
$plagiarism_table = $wpdb->prefix . ‘cdm_plagiarism_results’;
$stats[‘plagiarism_checks_today’] = $wpdb->get_var(
“SELECT COUNT(*) FROM $plagiarism_table
WHERE DATE(scan_date) = CURDATE()”
);

// Cache hit rate
$imdb_table = $wpdb->prefix . ‘cdm_imdb_cache’;
$stats[‘cached_movies’] = $wpdb->get_var(“SELECT COUNT(*) FROM $imdb_table”);

return $stats;
}

private static function get_user_ip() {
$ip_keys = [‘HTTP_CF_CONNECTING_IP’, ‘HTTP_X_FORWARDED_FOR’, ‘HTTP_X_FORWARDED’, ‘HTTP_FORWARDED_FOR’, ‘HTTP_FORWARDED’, ‘REMOTE_ADDR’];

foreach ($ip_keys as $key) {
if (!empty($_SERVER[$key])) {
$ip = $_SERVER[$key];
if (strpos($ip, ‘,’) !== false) {
$ip = explode(‘,’, $ip)[0];
}
return trim($ip);
}
}

return ‘0.0.0.0’;
}

public static function drop_tables() {
global $wpdb;

$tables = [
$wpdb->prefix . ‘cdm_draft_sections’,
$wpdb->prefix . ‘cdm_plagiarism_results’,
$wpdb->prefix . ‘cdm_imdb_cache’,
$wpdb->prefix . ‘cdm_activity_log’
];

foreach ($tables as $table) {
$wpdb->query(“DROP TABLE IF EXISTS $table”);
}
}

public static function debug_options() {
$options = [
‘cdm_youtube_api_key’,
‘cdm_groq_api_key’,
‘cdm_auto_save_interval’,
‘cdm_plagiarism_min_words’
];

error_log(‘=== CDM Options Debug ===’);
foreach ($options as $option) {
$value = get_option($option, ‘NOT_SET’);
error_log(“$option: ” . (is_string($value) ? $value : json_encode($value)));
}
error_log(‘=== End CDM Options Debug ===’);
}

}

“`

## **Environment Notes**

– Local WordPress server with Docker containerization
– Python 3 environment required for transcript extraction
– Multiple API integrations: IMDB, TMDB, YouTube, Claude, Groq
– Custom post type ‘content-draft’ registered and functional
– Auto-save system operational with 30-second intervals

## **Testing Status**

– [x] Plugin activation/deactivation – Passed
– [x] IMDB data fetching – Passed
– [x] YouTube transcript extraction – Passed
– [x] Claude AI integration – Passed
– [x] Auto-save functionality – Passed
– [x] Settings page API key storage – Passed
– [ ] Sequential tab navigation – **Issue: Skips Tab 2**
– [ ] Access meta box removal – **Issue: Still visible**
– [ ] Dashboard layout consistency – **Issue: Alignment problems**
– [ ] JavaScript event handler conflicts – **Issue: Duplicate handlers**

## **Critical Issues Requiring Resolution**

1. **Navigation Bug:** JavaScript event handler duplication causing Tab 1 → Tab 3 skip
2. **Meta Box Visibility:** Access meta box removal incomplete
3. **Layout Consistency:** Dashboard grid alignment needs CSS corrections
4. **UI Polish:** Visual bugs affecting user experience

## **Feature Implementation Status**

**Core Functionality:** ✅ Complete
– 5-step workflow system
– IMDB/TMDB data integration
– YouTube transcript fetching
– Claude AI content generation
– Auto-save system
– Settings management

**User Interface:** ⚠️ Functional with known issues
– Sequential navigation bug
– Visual alignment problems
– Meta box removal incomplete

**API Integrations:** ✅ Complete
– All external APIs operational
– Error handling implemented
– Security validation active

## **Recommended Next Steps**

1. **Fix navigation JavaScript** – Remove duplicate event handlers in admin.js
2. **Complete meta box removal** – Enhance global $wp_metaboxes manipulation
3. **Correct CSS alignment** – Add align-items: start to dashboard grid
4. **Test complete workflow** – Verify end-to-end functionality post-fixes

**Estimated Time to Resolution:** 2-3 hours for all identified bugs

This CRS accurately reflects the current state based on actual code analysis rather than optimistic projections.

[1] https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/collection_248f1c9a-817f-46c3-8711-5e4274de638b/38d3fc81-61ec-4147-9ee2-2355b3c38231/Ai_Development_Instructions.md
[2] https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/collection_248f1c9a-817f-46c3-8711-5e4274de638b/0cd0c001-f0de-4444-b88b-6ceb8b409337/Project-Wordpress-Content-Draft-Manager-Historic-Thread.md.md
[3] https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/collection_248f1c9a-817f-46c3-8711-5e4274de638b/02a13522-3b16-4785-9986-76423c391a01/Project_Wordpress_Content_Draft_Manager_CRS_V1.md
[4] https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/collection_248f1c9a-817f-46c3-8711-5e4274de638b/b3a5d569-7da5-49cd-af89-99489661a716/class-draft-manager.php_code.txt
[5] https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/collection_248f1c9a-817f-46c3-8711-5e4274de638b/aaff3a93-7222-4a57-945d-4beafbf76594/class-draft-form.php_code.txt
[6] https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/collection_248f1c9a-817f-46c3-8711-5e4274de638b/69e7fcd8-2638-4185-bada-b5302f61142e/Plugin-File-Structure.txt
[7] https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/collection_248f1c9a-817f-46c3-8711-5e4274de638b/5d0871d6-fb77-4699-8010-78fa353ac548/admin.js_code.txt
[8] https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/collection_248f1c9a-817f-46c3-8711-5e4274de638b/4a1cba97-eb59-4038-a305-6b1902a50212/class-groq-ai-detector.php_code.txt
[9] https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/collection_248f1c9a-817f-46c3-8711-5e4274de638b/cb7b2926-b8cc-4891-8c8e-e4c347867de6/class-database.php_code.txt
[10] https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/collection_248f1c9a-817f-46c3-8711-5e4274de638b/92cd46d5-654c-4cba-b720-3f4ae455ee8e/dashboard.php_code.txt
[11] https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/collection_248f1c9a-817f-46c3-8711-5e4274de638b/ec62962b-1a45-4f13-955d-d70704d4b73f/content-draft-manager.php_code.txt