cache.inc.php
1 month ago
landing.inc.php
1 month ago
skingenerator.inc.php
1 month ago
tools.inc.php
1 month ago
skingenerator.inc.php
178 lines
| 1 | <?php |
| 2 | if ( !is_admin() ) { |
| 3 | print 'Direct access not allowed.'; |
| 4 | exit; |
| 5 | } |
| 6 | |
| 7 | if ( ! class_exists( 'WCMP_SKIN_GENERATOR' ) ) { |
| 8 | class WCMP_SKIN_GENERATOR { |
| 9 | |
| 10 | static private $gemini_model = "models/gemini-3.5-flash"; |
| 11 | static private $gemini_url = "https://generativelanguage.googleapis.com/v1beta/"; |
| 12 | |
| 13 | static private function gemini_inference( $prompt, $api_key ) { |
| 14 | $inference_url = self::$gemini_url . self::$gemini_model . ":generateContent?key=" . $api_key; |
| 15 | $data = [ 'contents' => [[ 'parts' => [[ 'text' => $prompt ]] ]], "generationConfig" => [ "temperature" => 0.0, "topP" => 0.9 ] ]; |
| 16 | |
| 17 | // Inferring the model to generate the form. |
| 18 | $body = json_encode( $data ); |
| 19 | |
| 20 | $response = wp_remote_post( $inference_url, [ |
| 21 | 'headers' => [ 'Content-Type' => 'application/json' ], |
| 22 | 'body' => $body, |
| 23 | 'timeout' => 120, |
| 24 | ]); |
| 25 | |
| 26 | if ( is_wp_error( $response ) ) { |
| 27 | throw new Exception( $response->get_error_message() ); |
| 28 | } |
| 29 | |
| 30 | $exception = new Exception( __( 'Empty AI model answer', 'music-player-for-woocommerce' ) ); |
| 31 | |
| 32 | $data = json_decode(wp_remote_retrieve_body($response), true); |
| 33 | if( empty( $data ) ) throw $exception; |
| 34 | if ( ! empty( $data[ 'error' ] ) ) throw new Exception( $data['error']['message'] ); |
| 35 | |
| 36 | try { |
| 37 | $output = $data['candidates'][0]['content']['parts'][0]['text']; |
| 38 | // Remove markdown characters from the beginning and end: |
| 39 | $output = preg_replace('/^```css\s*(.*?)\s*```$/s', '$1', $output); |
| 40 | $output = str_replace( '.wcmp-custom-skin .mejs-container', '.wcmp-custom-skin.mejs-container', $output ); |
| 41 | } catch ( Exception $err ) { |
| 42 | throw $exception; |
| 43 | } |
| 44 | return $output; |
| 45 | } // End gemini_inference. |
| 46 | |
| 47 | static private function wp_connector_inference( $prompt ) { |
| 48 | $_set_timeout = function($timeout){ return 120; }; |
| 49 | add_filter('wp_ai_client_default_request_timeout', $_set_timeout, 999); |
| 50 | |
| 51 | try { |
| 52 | $output = wp_ai_client_prompt($prompt)->generate_text(); |
| 53 | } finally { |
| 54 | remove_filter('wp_ai_client_default_request_timeout', $_set_timeout, 999); |
| 55 | } |
| 56 | |
| 57 | if (is_wp_error($output)) throw new Exception( $output->get_error_message() ); |
| 58 | return $output; |
| 59 | } // End wp_connector_inference. |
| 60 | |
| 61 | static public function is_wp_connector_available() { |
| 62 | // Check if there are models registered using the WordPress Connector API. |
| 63 | if ( function_exists('wp_ai_client_prompt') ) { |
| 64 | $builder = wp_ai_client_prompt('test'); |
| 65 | if ($builder->is_supported_for_text_generation()) { |
| 66 | return true; |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | return false; |
| 71 | } // End is_wp_connector_available |
| 72 | |
| 73 | static public function model_inference( $inference_data ) { |
| 74 | |
| 75 | // Base CSS. |
| 76 | $base_url = includes_url() . 'js/mediaelement/'; |
| 77 | $base_css_url = $base_url . 'wp-mediaelement.css'; |
| 78 | $base_css_path = ABSPATH . WPINC . '/js/mediaelement/mediaelementplayer-legacy.css'; |
| 79 | $schema = file_exists( $base_css_path ) ? file_get_contents( $base_css_path ) : $base_css_url; |
| 80 | $schema = str_ireplace( 'url(', 'url(' . $base_url, $schema ); |
| 81 | |
| 82 | // Update the URLs of |
| 83 | // Generate the prompt. |
| 84 | $prompt = "Generate CSS styles for a MediaElementPlayer skin based on the CSS styles:\n\n$schema\n\n" |
| 85 | . "The following rules ALWAYS apply regardless of the skin description:\n" |
| 86 | . "- Only modify color, background, border-radius, box-shadow, and font properties.\n" |
| 87 | . "- Do NOT alter position, z-index, height, width, display, transform, or animation values.\n" |
| 88 | . "- Preserve all SVG background-image URLs exactly as they appear in the source CSS.\n" |
| 89 | . "- Do not add selectors or properties that are not present in the source CSS.\n" |
| 90 | . "- Infer a coherent color palette from the description and apply it consistently across all components (control bar, progress bar, buttons, volume slider, captions).\n\n" |
| 91 | . "Output instructions:\n" |
| 92 | . "Return only valid CSS code scoped under .wcmp-custom-skin. No comments, no explanations, no description, no extra information, no Markdown, no enclose the response in triple backticks.\n\n" |
| 93 | . "Skin description:\n{$inference_data['skin_description']}"; |
| 94 | |
| 95 | if ( self::is_wp_connector_available() ) { |
| 96 | $output = self::wp_connector_inference( $prompt ); |
| 97 | } else if ( ! empty( $inference_data['api_key'] ) ) { |
| 98 | $output = self::gemini_inference( $prompt, $inference_data['api_key'] ); |
| 99 | } else { |
| 100 | throw new Exception( __( 'There is no enought information to generate the custom skin', 'music-player-for-woocommerce' ) ); |
| 101 | } |
| 102 | |
| 103 | return $output; |
| 104 | |
| 105 | } // End model_inference. |
| 106 | |
| 107 | } // End class WCMP_SKIN_GENERATOR. |
| 108 | } |
| 109 | |
| 110 | // Main code |
| 111 | |
| 112 | /** CALL THE AI SKIN GENERATOR **/ |
| 113 | if ( ! empty( $_POST['wcmp_skin_generator_description'] ) ) { |
| 114 | |
| 115 | remove_all_actions( 'shutdown' ); |
| 116 | check_admin_referer( 'wcmp-ai-skin-generator', '_wcmp_nonce' ); |
| 117 | |
| 118 | function prepare_inference_data() { |
| 119 | $output = []; |
| 120 | |
| 121 | if ( current_user_can( 'manage_options' ) ) new Exception(__( 'You don\'t have enought privileges to generate custom skins.', 'music-player-for-woocommerce' )); |
| 122 | |
| 123 | if ( ! WCMP_SKIN_GENERATOR::is_wp_connector_available() ) { |
| 124 | if ( ! isset( $_POST['wcmp_skin_generator_api_key'] ) ) new Exception( __( 'The AI Provider API Key is required.', 'music-player-for-woocommerce' )); |
| 125 | |
| 126 | $api_key = sanitize_text_field( wp_unslash( $_POST['wcmp_skin_generator_api_key'] ) ); |
| 127 | if ( empty( $api_key ) ) new Exception( __( 'The AI Provider API Key is required.', 'music-player-for-woocommerce' )); |
| 128 | $output['api_key'] = $api_key; |
| 129 | } |
| 130 | |
| 131 | $skin_description = sanitize_textarea_field( wp_unslash( $_POST['wcmp_skin_generator_description'] ) ); |
| 132 | if ( empty( $skin_description ) ) new Exception( __( 'The Skin description is required.', 'music-player-for-woocommerce' )); |
| 133 | $output['skin_description'] = $skin_description; |
| 134 | |
| 135 | return $output; |
| 136 | } |
| 137 | |
| 138 | try { |
| 139 | $inference_data = prepare_inference_data(); |
| 140 | $output['success'] = WCMP_SKIN_GENERATOR::model_inference( $inference_data ); |
| 141 | } catch( Exception $err ) { |
| 142 | $output['error'] = $err->getMessage(); |
| 143 | } |
| 144 | |
| 145 | print json_encode( $output ); |
| 146 | exit; |
| 147 | } elseif ( |
| 148 | ! empty( $_POST['wcmp_custom_skin'] ) |
| 149 | ) { |
| 150 | remove_all_actions( 'shutdown' ); |
| 151 | check_admin_referer( 'wcmp-custom-skin-preview', '_wcmp_nonce' ); |
| 152 | |
| 153 | $custom_skin = sanitize_textarea_field( wp_unslash( $_POST['wcmp_custom_skin'] ) ); |
| 154 | // Re-check if custom_skin is not empty. |
| 155 | if ( ! empty( $custom_skin ) ) { |
| 156 | print '<link rel="stylesheet" href="'. esc_attr( includes_url( 'js/mediaelement/mediaelementplayer-legacy.min.css' ) ) .'"> |
| 157 | <link rel="stylesheet" href="' . esc_attr( includes_url( 'js/mediaelement/wp-mediaelement.min.css' ) ) .'"> |
| 158 | <link rel="stylesheet" href="' . esc_attr( plugin_dir_url( __FILE__ ) . '../vendors/mejs-skins/mejs-skins.min.css' ) . '"> |
| 159 | <style> |
| 160 | body {margin: 0;padding: 20px;font-family: Arial, sans-serif;background: #ffffff;} |
| 161 | .player-container {max-width: 600px;margin: 0 auto;} |
| 162 | .mejs__container {margin: 0 auto;}' . |
| 163 | $custom_skin . |
| 164 | '</style> |
| 165 | <script type="text/javascript" src="' . esc_attr( includes_url( 'js/jquery/jquery.js' ) ) . '"></script> |
| 166 | <script src="' . esc_attr( includes_url( 'js/jquery/jquery-migrate.min.js' ) ) . '"></script> |
| 167 | <script src="' . esc_attr( includes_url( 'js/mediaelement/mediaelement-and-player.min.js' ) ) . '"></script> |
| 168 | <script src="' . esc_attr( includes_url( 'js/mediaelement/mediaelement-migrate.js' ) ) . '"></script> |
| 169 | <script src="' . esc_attr( includes_url( 'js/mediaelement/wp-mediaelement.js' ) ) . '"></script> |
| 170 | <script src="' . esc_attr( plugin_dir_url( __FILE__ ) . '../js/public.js' ) . '"></script> |
| 171 | <div class="wcmp-player-container"> |
| 172 | <audio controlslist="nodownload" volume="1" preload="none" data-lazyloading="none" class="wcmp-player mejs-classic wcmp-custom-skin" src="'. esc_attr( plugin_dir_url( __FILE__ ) . '../vendors/demo/demo.mp3' ) . '"> |
| 173 | <source src="' . esc_attr( plugin_dir_url( __FILE__ ) . '../vendors/demo/demo.mp3' ) . '" type="audio/mp3"> |
| 174 | </audio> |
| 175 | </div>'; |
| 176 | exit; |
| 177 | } |
| 178 | } |