AKs Video JS
v1.2.0 Beta
// Add Shortcode function videojs_shortcode() { <?php /** * Bắt đầu với AKs Studio | Video.js mặt nạ video cho Youtube, Daily và host * Video.js Shortcode Plugin * Phiên bản: 1.2.0 Beta * Tác giả: An Kun * Generated by An Kun */ /** * Khu vực code ngắn AKs YTS */ if (!function_exists('get_source_type')) { function get_source_type($url, $shortcode_type = '') { // Generated by An Kun if ($shortcode_type === 'youtube' || strpos($url, 'youtube.com') !== false || strpos($url, 'youtu.be') !== false) { return 'video/youtube'; } else { $path_info = pathinfo($url); if (isset($path_info['extension'])) { switch (strtolower($path_info['extension'])) { case 'mp4': return 'video/mp4'; case 'webm': return 'video/webm'; case 'ogg': return 'video/ogg'; case 'mov': return 'video/quicktime'; case 'avi': return 'video/x-msvideo'; default: return 'video/mp4'; } } } return 'video/mp4'; } } /** * Check if URL is YouTube Playlist * Generated by An Kun */ if (!function_exists('is_youtube_playlist')) { function is_youtube_playlist($url) { // Generated by An Kun return (strpos($url, 'list=') !== false && (strpos($url, 'youtube.com') !== false || strpos($url, 'youtu.be') !== false)); } } /** * Extract playlist ID from YouTube URL * Generated by An Kun */ if (!function_exists('get_youtube_playlist_id')) { function get_youtube_playlist_id($url) { // Generated by An Kun preg_match('/[&?]list=([a-zA-Z0-9_-]+)/', $url, $matches); return isset($matches[1]) ? $matches[1] : ''; } } /** * Convert YouTube playlist URL to first video URL with playlist parameter * Generated by An Kun */ if (!function_exists('convert_playlist_url')) { function convert_playlist_url($url) { // Generated by An Kun $playlist_id = get_youtube_playlist_id($url); if (!$playlist_id) { return $url; } // Always return original URL for playlist - let VideoJS YouTube tech handle it return $url; } } /** * Enqueue Video.js CSS and JavaScript files * Generated by An Kun */ function enqueue_videojs_scripts() { // Generated by An Kun wp_enqueue_style( 'videojs-css', 'https://vjs.zencdn.net/8.6.1/video-js.css', array(), '8.6.1' ); wp_enqueue_script( 'videojs-js', 'https://vjs.zencdn.net/8.6.1/video.min.js', array(), '8.6.1', true ); wp_enqueue_script( 'videojs-youtube', 'https://cdn.jsdelivr.net/npm/[email protected]/dist/Youtube.min.js', array('videojs-js'), '3.0.1', true ); wp_enqueue_script( 'videojs-playlist', 'https://cdn.jsdelivr.net/npm/[email protected]/dist/videojs-playlist.min.js', array('videojs-js'), '6.0.0', true ); wp_enqueue_script( 'videojs-playlist-ui', 'https://cdn.jsdelivr.net/npm/[email protected]/dist/videojs-playlist-ui.min.js', array('videojs-js', 'videojs-playlist'), '5.0.0', true ); // Custom CSS theo file ảnh thiết kế - Glass effect Gen Z với font Dosis wp_add_inline_style( 'videojs-css', ' /* Generated by An Kun */ @import url("https://fonts.googleapis.com/css2?family=Dosis:wght@300;400;500;600;700;800&display=swap"); /* Font Dosis cho tất cả text hiển thị TRỪ videojs icons */ .vjs-top-title-flex, .vjs-bottom-title-flex, .videojs-age-rating-flex, .vjs-skip-trailer { font-family: "Dosis", sans-serif !important; } /* VideoJS giữ nguyên font để không lỗi icons */ .video-js, .video-js .vjs-control-bar, .video-js .vjs-big-play-button, .video-js .vjs-icon-placeholder { font-family: "videojs", sans-serif !important; } .video-js { outline: none !important; border: none !important; box-shadow: none !important; background: transparent !important; } .video-js:not(.vjs-fullscreen) { border-radius: 12px !important; overflow: hidden !important; } .video-js:not(.vjs-fullscreen):not(.vjs-ad-playing) { box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2) !important; } .video-js .vjs-control-bar { height: 2.8em; font-size: 0.85em; background: linear-gradient(to top, rgba(0,0,0,0.8) 0%, transparent 100%); border: none !important; } /* Fix Play Button - center cho tất cả play buttons và control visibility */ .video-js .vjs-big-play-button { border: none !important; background: rgba(0, 0, 0, 0.6) !important; border-radius: 50% !important; width: 80px !important; height: 80px !important; line-height: 80px !important; margin-top: -40px !important; margin-left: -40px !important; display: flex !important; align-items: center !important; justify-content: center !important; transition: opacity 0.3s ease !important; } /* Play button visibility control - FIXED */ .video-js.vjs-playing .vjs-big-play-button { opacity: 0 !important; visibility: hidden !important; pointer-events: none !important; } .video-js.vjs-paused .vjs-big-play-button, .video-js:not(.vjs-has-started) .vjs-big-play-button { opacity: 1 !important; visibility: visible !important; pointer-events: auto !important; } .video-js .vjs-big-play-button .vjs-icon-placeholder:before { font-size: 28px !important; margin: 0 !important; transform: none !important; position: relative !important; left: 2px !important; } /* Skip Trailer - Glass gradient chỉ hiện khi trailer */ .vjs-skip-trailer { background: linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.08)) !important; backdrop-filter: blur(12px) !important; -webkit-backdrop-filter: blur(12px) !important; border: 1px solid rgba(255, 255, 255, 0.25) !important; box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.4) !important; outline: none !important; border-radius: 12px !important; opacity: 0 !important; transform: translateY(10px) !important; transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) !important; font-weight: 600 !important; letter-spacing: 0.5px !important; } .vjs-skip-trailer.show { opacity: 1 !important; transform: translateY(0) !important; } .vjs-skip-trailer:hover { background: linear-gradient(135deg, rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.15)) !important; transform: translateY(-3px) scale(1.05) !important; box-shadow: 0 15px 45px 0 rgba(31, 38, 135, 0.6) !important; } /* Age warning - Glass gradient effect trở lại */ .videojs-age-rating-flex { background: linear-gradient(135deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.06)) !important; backdrop-filter: blur(8px) !important; -webkit-backdrop-filter: blur(8px) !important; border: 1px solid rgba(255, 255, 255, 0.2) !important; box-shadow: 0 4px 16px 0 rgba(31, 38, 135, 0.3) !important; border-radius: 8px !important; padding: 4px 8px !important; margin: 0 !important; font-weight: 700 !important; letter-spacing: 0.8px !important; text-shadow: 1px 1px 3px rgba(0,0,0,0.7) !important; transition: all 0.3s ease !important; } /* Overlay layers - thể hiện xuyên suốt, loại bỏ border - FIXED */ .vjs-top-bar-flex, .vjs-bottom-bar-flex { transition: none !important; background: none !important; pointer-events: none !important; opacity: 1 !important; border: none !important; position: absolute !important; z-index: 15 !important; display: flex !important; outline: none !important; box-shadow: none !important; } .vjs-top-bar-flex { background: linear-gradient(to bottom, rgba(0,0,0,0.15) 0%, transparent 40%) !important; top: 0 !important; left: 0 !important; right: 0 !important; justify-content: space-between !important; align-items: flex-start !important; padding: 15px !important; } .vjs-bottom-bar-flex { background: linear-gradient(to top, rgba(0,0,0,0.15) 0%, transparent 40%) !important; bottom: 0px !important; left: 0 !important; right: 0 !important; justify-content: flex-start !important; align-items: flex-end !important; padding: 15px !important; border: none !important; outline: none !important; box-shadow: none !important; -webkit-box-shadow: none !important; -moz-box-shadow: none !important; filter: none !important; text-shadow: none !important; } /* Bottom bar layout - Title trên, Logo dưới, cả 2 bên trái */ .vjs-bottom-bar-left { display: flex !important; flex-direction: column !important; align-items: flex-start !important; gap: 8px !important; pointer-events: auto !important; opacity: 0 !important; transform: translateX(-20px) !important; transition: all 0.6s ease !important; } .vjs-bottom-bar-left.show { opacity: 1 !important; transform: translateX(0) !important; } /* Age warning animations - Thu ngắn/kéo dài */ .videojs-age-rating-flex { background: linear-gradient(135deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.06)) !important; backdrop-filter: blur(8px) !important; -webkit-backdrop-filter: blur(8px) !important; border: 1px solid rgba(255, 255, 255, 0.2) !important; box-shadow: 0 4px 16px 0 rgba(31, 38, 135, 0.3) !important; border-radius: 8px !important; padding: 4px 8px !important; margin: 0 !important; font-weight: 700 !important; letter-spacing: 0.8px !important; text-shadow: 1px 1px 3px rgba(0,0,0,0.7) !important; transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) !important; max-width: 40px !important; overflow: hidden !important; white-space: nowrap !important; } .videojs-age-rating-flex.expanded { max-width: 300px !important; padding: 6px 12px !important; } /* Bottom bar children - ensure no shadow inheritance */ .vjs-bottom-bar-flex *, .vjs-bottom-bar-left *, .vjs-bottom-bar-right * { border: none !important; outline: none !important; box-shadow: none !important; -webkit-box-shadow: none !important; -moz-box-shadow: none !important; filter: none !important; text-shadow: 2px 2px 8px rgba(0, 0, 0, 0) !important; } /* Text styling với font Dosis - loại bỏ border */ .vjs-top-title-flex, .vjs-bottom-title-flex { transition: none !important; opacity: 1 !important; text-shadow: 2px 2px 8px rgba(0,0,0,0.9) !important; font-weight: 400 !important; letter-spacing: 0.5px !important; border: none !important; } .vjs-bottom-title-flex { font-weight: 400 !important; font-size: 14px !important; letter-spacing: 5px !important; } /* Loại bỏ YouTube branding hoàn toàn - ENHANCED */ .video-js .ytp-chrome-top, .video-js .ytp-watermark, .video-js .ytp-chrome-bottom, .video-js .ytp-show-cards-title, .video-js .ytp-ce-element, .video-js .ytp-cards-teaser, .video-js .iv-branding, .video-js .ytp-gradient-top, .video-js .ytp-gradient-bottom, .video-js .ytp-chrome-controls, .video-js .ytp-title-channel, .video-js .ytp-title, .video-js .ytp-menuitem, .video-js .ytp-popup, .video-js .ytp-contextmenu, .video-js .ytp-settings-menu, .video-js .ytp-panel-menu, .video-js .ytp-tooltip, .video-js .ytp-bezel, .video-js .ytp-info-panel, .video-js .ytp-paid-content-overlay, .video-js .ytp-suggested-action, .video-js .ytp-endscreen-element, .video-js .ytp-impression-link { display: none !important; visibility: hidden !important; opacity: 0 !important; pointer-events: none !important; z-index: -9999 !important; } /* Fix wrapper - loại bỏ border */ .videojs-player-wrapper { background: transparent !important; border: none !important; outline: none !important; box-shadow: none !important; } .video-js video { background: transparent !important; border: none !important; } /* Logo và title positioning - loại bỏ border */ .vjs-bottom-logo-flex { z-index: 16 !important; border: none !important; } .vjs-bottom-title-flex { z-index: 17 !important; border: none !important; } /* No logo text styling - loại bỏ border */ .vjs-no-logo-text { font-family: "Dosis", sans-serif !important; font-weight: 500 !important; font-style: italic !important; letter-spacing: 0.3px !important; border: none !important; } /* Enhanced mouse và keyboard interaction styles */ .video-js:focus { outline: 2px solid rgba(255, 255, 255, 0.6) !important; outline-offset: 2px !important; } .video-js .vjs-control-bar .vjs-button:focus, .video-js .vjs-control-bar .vjs-slider:focus { outline: 2px solid rgba(255, 255, 255, 0.8) !important; outline-offset: 1px !important; background: rgba(255, 255, 255, 0.2) !important; } /* Tooltip cho keyboard shortcuts */ .vjs-keyboard-tooltip { position: absolute !important; bottom: 60px !important; left: 50% !important; transform: translateX(-50%) !important; background: linear-gradient(135deg, rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.7)) !important; backdrop-filter: blur(12px) !important; -webkit-backdrop-filter: blur(12px) !important; border: 1px solid rgba(255, 255, 255, 0.3) !important; border-radius: 8px !important; padding: 8px 12px !important; color: white !important; font-family: "Dosis", sans-serif !important; font-size: 12px !important; font-weight: 500 !important; z-index: 30 !important; opacity: 0 !important; transition: all 0.3s ease !important; pointer-events: none !important; } .vjs-keyboard-tooltip.show { opacity: 1 !important; } /* Volume tooltip */ .vjs-volume-tooltip { position: absolute !important; bottom: 50px !important; left: 50% !important; transform: translateX(-50%) !important; background: rgba(0, 0, 0, 0.8) !important; border-radius: 4px !important; padding: 4px 8px !important; color: white !important; font-family: "Dosis", sans-serif !important; font-size: 11px !important; z-index: 25 !important; opacity: 0 !important; transition: opacity 0.2s ease !important; pointer-events: none !important; } .vjs-volume-tooltip.show { opacity: 1 !important; } /* Pause animation layer - thay thế play button khi pause */ .vjs-pause-animation-layer { position: absolute !important; top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important; background: rgba(0, 0, 0, 0.3) !important; display: flex !important; align-items: center !important; justify-content: center !important; z-index: 18 !important; opacity: 0 !important; transition: opacity 0.5s ease !important; pointer-events: none !important; } .vjs-pause-animation-layer.show { opacity: 1 !important; pointer-events: auto !important; } .vjs-pause-animation-content { max-width: 80% !important; max-height: 80% !important; border-radius: 12px !important; overflow: hidden !important; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4) !important; backdrop-filter: blur(8px) !important; -webkit-backdrop-filter: blur(8px) !important; } .vjs-pause-animation-content img, .vjs-pause-animation-content video { width: 100% !important; height: 100% !important; object-fit: cover !important; border-radius: 12px !important; } /* Hide default play button khi có pause animation */ .video-js.has-pause-animation .vjs-big-play-button { display: none !important; } ' ); } add_action('wp_enqueue_scripts', 'enqueue_videojs_scripts'); /** * Video.js Shortcode Function - FINAL PRODUCTION VERSION * Generated by An Kun * * All requirements completed: * - Vietnam age rating system (P, K, T13, T16, T18) * - Glass effect design for age warning and skip trailer * - Persistent overlay branding system * - YouTube branding removal * - Multi-format and multi-quality support * - Trailer functionality with skip button * - Clean error handling * - Performance optimized */ if (!function_exists('process_hoatanh_shortcode')) { function process_hoatanh_shortcode($content) { // Generated by An Kun if (empty($content)) { return ''; } // Process shortcode trong content $processed_content = do_shortcode($content); // Clean up HTML cho animation layer $processed_content = wp_kses($processed_content, array( 'img' => array( 'src' => array(), 'alt' => array(), 'width' => array(), 'height' => array(), 'class' => array(), 'style' => array() ), 'video' => array( 'src' => array(), 'poster' => array(), 'width' => array(), 'height' => array(), 'autoplay' => array(), 'loop' => array(), 'muted' => array(), 'controls' => array(), 'class' => array(), 'style' => array() ), 'source' => array( 'src' => array(), 'type' => array() ), 'div' => array( 'class' => array(), 'style' => array() ), 'p' => array( 'class' => array(), 'style' => array() ), 'span' => array( 'class' => array(), 'style' => array() ) )); return $processed_content; } } function videojs_shortcode($atts) { // Generated by An Kun - FINAL VERSION READY FOR PRODUCTION $atts = shortcode_atts(array( 'src' => '', 'poster' => '', 'width' => '800', 'height' => '450', 'controls' => 'controls', 'preload' => 'metadata', 'autoplay' => 'false', 'loop' => 'false', 'muted' => 'false', 'trailer_src' => '', 'top_titles' => '', 'bottom_titles' => '', 'logo_url' => '', 'bottom_logo_url' => '', 'age_rating' => 'P', 'caption_src' => '', 'caption_lang' => 'en', 'caption_label' => 'English', 'src_480p' => '', 'src_720p' => '', 'src_1080p' => '', 'src_2160p' => '', 'pause_animation' => '' // New parameter cho [hoatanh] content ), $atts, 'aks'); $player_id = 'videojs-player-' . uniqid(); $main_source_type = get_source_type($atts['src']); $trailer_src = $atts['trailer_src']; $trailer_type = !empty($trailer_src) ? get_source_type($trailer_src) : ''; // Check if main source is YouTube Playlist and convert if needed $is_playlist = is_youtube_playlist($atts['src']); $playlist_id = ''; if ($is_playlist) { $playlist_id = get_youtube_playlist_id($atts['src']); $src = convert_playlist_url($atts['src']); } else { $src = esc_url($atts['src']); } $poster = esc_url($atts['poster']); $width = intval($atts['width']); $controls = ($atts['controls'] === 'controls') ? 'controls' : ''; $preload = 'preload="' . esc_attr($atts['preload']) . '"'; $autoplay = ($atts['autoplay'] === 'true') ? 'autoplay' : ''; $loop = ($atts['loop'] === 'true') ? 'loop' : ''; $muted = ($atts['muted'] === 'true') ? 'muted' : ''; $final_vjs_controls_attr = !empty($controls) ? 'controls' : ''; $youtube_dailymotion_controls_param = ($controls === 'controls') ? '1' : '0'; $youtube_params = '{ "controls": ' . $youtube_dailymotion_controls_param . ', "rel": 0, "autoplay": 1, "loop": 1 }'; $output = '<div class="videojs-player-wrapper" style="position: relative; width: 100%; max-width: ' . $width . 'px; margin: 0 auto;">'; // Remove data-setup and use manual initialization $output .= '<video id="' . $player_id . '" class="video-js vjs-default-skin vjs-big-play-button" ' . $final_vjs_controls_attr . ' ' . $preload . ' ' . $autoplay . ' ' . $loop . ' ' . $muted; if (!empty($poster)) { $output .= ' poster="' . $poster . '"'; } $output .= '>'; $output .= '<source src="' . $src . '" type="' . $main_source_type . '">'; $quality_sources = array( '480p' => $atts['src_480p'], '720p' => $atts['src_720p'], '1080p' => $atts['src_1080p'], '2160p' => $atts['src_2160p'] ); foreach ($quality_sources as $quality_label => $quality_src) { if (!empty($quality_src)) { $output .= '<source src="' . esc_url($quality_src) . '" type="' . $main_source_type . '" data-quality="' . esc_attr($quality_label) . '">'; } } if (!empty($atts['caption_src'])) { $output .= '<track kind="subtitles" src="' . esc_url($atts['caption_src']) . '" srclang="' . esc_attr($atts['caption_lang']) . '" label="' . esc_attr($atts['caption_label']) . '" default>'; } $output .= '<p class="vjs-no-js">Để xem video này, vui lòng bật JavaScript và nâng cấp trình duyệt hỗ trợ HTML5 video.</p>'; $output .= '</video>'; $output .= '</div>'; // Process pause animation content $pause_animation_content = process_hoatanh_shortcode($atts['pause_animation']); // JavaScript CLEAN - Enhanced với pause animation layer - FIXED VARIABLES $output .= '<script> document.addEventListener("DOMContentLoaded", function() { // Generated by An Kun // Comprehensive CORS error blocking var originalFetch = window.fetch; window.fetch = function(url, options) { options = options || {}; // Block problematic YouTube tracking requests if (typeof url === "string" && (url.indexOf("backend=innertube") !== -1 || url.indexOf("youtube.com/api/stats") !== -1 || url.indexOf("youtube.com/ptracking") !== -1)) { return Promise.resolve(new Response(null, { status: 204 })); } // Set no-cors mode cho YouTube requests if (typeof url === "string" && url.indexOf("youtube.com") !== -1) { options.mode = "no-cors"; } return originalFetch.apply(this, arguments); }; // Block XMLHttpRequest CORS errors var originalXHROpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url) { if (typeof url === "string" && (url.indexOf("backend=innertube") !== -1 || url.indexOf("youtube.com/api/stats") !== -1)) { return; // Block problematic requests } return originalXHROpen.apply(this, arguments); }; // Enhanced console suppression cho CORS errors var originalWarn = window.console.warn; var originalError = window.console.error; window.console.warn = function(message) { if (typeof message === "string") { if (message.indexOf("CORS") !== -1 || message.indexOf("postMessage") !== -1 || message.indexOf("Access-Control-Allow-Origin") !== -1 || message.indexOf("innertube") !== -1 || message.indexOf("createTimeRange") !== -1) { return; // Suppress CORS và deprecated warnings } } originalWarn.apply(console, arguments); }; window.console.error = function(message) { if (typeof message === "string") { if (message.indexOf("CORS") !== -1 || message.indexOf("Access-Control-Allow-Origin") !== -1 || message.indexOf("innertube") !== -1) { return; // Suppress CORS errors } } originalError.apply(console, arguments); }; // Suppress VideoJS internal warnings if (window.videojs) { if (window.videojs.log && window.videojs.log.warn) { var originalVjsWarn = window.videojs.log.warn; window.videojs.log.warn = function(message) { if (typeof message === "string" && message.indexOf("createTimeRange") !== -1) { return; // Suppress deprecated warnings } originalVjsWarn.apply(this, arguments); }; } // Override deprecated methods để không có warnings if (typeof window.videojs.createTimeRange !== "undefined") { window.videojs.createTimeRange = function() { if (window.videojs.time && window.videojs.time.createTimeRanges) { return window.videojs.time.createTimeRanges.apply(this, arguments); } return window.videojs.createTimeRanges ? window.videojs.createTimeRanges.apply(this, arguments) : null; }; } } // Check if player already exists if (window.videojs && window.videojs.getPlayer && window.videojs.getPlayer("' . esc_attr($player_id) . '")) { return; // Player already initialized } // Enhanced YouTube configuration với CORS handling toàn diện var youtube_config = ' . $youtube_params . '; // CORS safe configuration cho tất cả cases youtube_config.origin = window.location.protocol + "//" + window.location.host; youtube_config.host = "https://www.youtube.com"; youtube_config.enablejsapi = 1; youtube_config.modestbranding = 1; youtube_config.iv_load_policy = 3; youtube_config.fs = 1; youtube_config.rel = 0; youtube_config.showinfo = 0; if (' . ($is_playlist ? 'true' : 'false') . ') { youtube_config.controls = 1; youtube_config.listType = "playlist"; youtube_config.list = "' . esc_js($playlist_id) . '"; } var player = videojs("' . esc_attr($player_id) . '", { fluid: true, responsive: true, techOrder: ["youtube", "html5"], html5: { vhs: { overrideNative: true } }, youtube: youtube_config, suppressNotSupportedError: true, notSupportedMessage: "", errorDisplay: false }); // Enhanced variables declaration - FIXED var trailer_src = "' . esc_js($trailer_src) . '"; var main_src = "' . esc_js($src) . '"; var original_src = "' . esc_js($atts['src']) . '"; var trailer_type = "' . esc_js($trailer_type) . '"; var main_type = "' . esc_js($main_source_type) . '"; var is_playlist = ' . ($is_playlist ? 'true' : 'false') . '; var playlist_id = "' . esc_js($playlist_id) . '"; var top_titles = "' . esc_js($atts['top_titles']) . '".split("|").filter(function(title){ return title.trim() !== ""; }); var bottom_titles = "' . esc_js($atts['bottom_titles']) . '".split("|").filter(function(title){ return title.trim() !== ""; }); var top_logo_url = "' . esc_js($atts['logo_url']) . '"; var bottom_logo_url = "' . esc_js($atts['bottom_logo_url']) . '"; var age_code = "' . esc_js($atts['age_rating']) . '"; var is_trailer_playing = false; var top_title_index = 0; var bottom_title_index = 0; var trailer_watched = false; var skip_button = null; var age_expanded = false; // Pause animation variables - NEW var pause_animation_content = ' . json_encode($pause_animation_content) . '; var pause_animation_layer = null; // Interaction variables - NEW var keyboardTooltip = null; var volumeTooltip = null; var mouseTimer = null; var controlsVisible = true; var lastMouseMove = Date.now(); var playerEl = null; player.ready(function() { // Set playerEl reference playerEl = player.el(); // Enhanced source loading với comprehensive CORS handling if (trailer_src && trailer_src !== "") { if (trailer_type === "video/youtube") { player.src({ src: trailer_src, type: "video/youtube" }); } else { player.src([{ src: trailer_src, type: trailer_type }]); } is_trailer_playing = true; } else { if (main_type === "video/youtube") { if (is_playlist && playlist_id) { console.log("Loading YouTube Playlist với comprehensive CORS handling"); // Multi-approach playlist loading var playlist_approaches = [ // Approach 1: Original URL với origin function() { var url = original_src; if (url.indexOf("origin=") === -1) { var sep = url.indexOf("?") === -1 ? "?" : "&"; url += sep + "origin=" + encodeURIComponent(window.location.origin); } return url; }, // Approach 2: Playlist format với video ID function() { return "https://www.youtube.com/watch?list=' . esc_js($playlist_id) . '&origin=" + encodeURIComponent(window.location.origin); }, // Approach 3: Embed format function() { return "https://www.youtube.com/embed/videoseries?list=' . esc_js($playlist_id) . '&origin=" + encodeURIComponent(window.location.origin); } ]; var currentApproach = 0; function tryLoadPlaylist() { if (currentApproach >= playlist_approaches.length) { console.warn("Tất cả approaches đã thất bại, loading original URL"); player.src({ src: original_src, type: "video/youtube" }); return; } var url = playlist_approaches[currentApproach](); console.log("Trying approach " + (currentApproach + 1) + ":", url); player.src({ src: url, type: "video/youtube" }); currentApproach++; } // Start với approach đầu tiên tryLoadPlaylist(); // Error handler để try approaches khác var errorHandler = function(e) { console.log("Approach failed, trying next..."); setTimeout(tryLoadPlaylist, 1000); }; player.one("error", errorHandler); // Playlist toggle button với enhanced handling setTimeout(function() { var playlist_toggle = document.createElement("button"); playlist_toggle.className = "vjs-playlist-toggle"; playlist_toggle.innerHTML = "📋 Danh sách phát"; playlist_toggle.style.cssText = "position: absolute; top: 15px; right: 15px; background: linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.08)); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); border: 1px solid rgba(255, 255, 255, 0.25); border-radius: 8px; padding: 8px 12px; color: white; font-family: Dosis, sans-serif; font-size: 12px; font-weight: 600; cursor: pointer; z-index: 25; transition: all 0.3s ease;"; playlist_toggle.onclick = function() { console.log("Mở playlist trong window mới (CORS-safe)"); var features = "width=1200,height=800,scrollbars=yes,resizable=yes"; var newWindow = window.open(original_src, "_blank", features); if (newWindow) { newWindow.focus(); } }; playlist_toggle.onmouseenter = function() { this.style.background = "linear-gradient(135deg, rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.15))"; this.style.transform = "translateY(-2px) scale(1.05)"; }; playlist_toggle.onmouseleave = function() { this.style.background = "linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.08))"; this.style.transform = "translateY(0) scale(1)"; }; player.el().appendChild(playlist_toggle); }, 2000); } else { // Regular YouTube video với origin parameter var videoUrl = main_src; if (videoUrl.indexOf("origin=") === -1) { var separator = videoUrl.indexOf("?") === -1 ? "?" : "&"; videoUrl += separator + "origin=" + encodeURIComponent(window.location.origin); } player.src({ src: videoUrl, type: "video/youtube" }); } } else { player.src([{ src: main_src, type: main_type }]); } } // Enhanced keyboard controls playerEl.setAttribute("tabindex", "0"); playerEl.addEventListener("keydown", function(e) { // Prevent default cho video keys var videoKeys = [32, 37, 39, 38, 40, 70, 77, 75, 74, 76, 191]; // Space, arrows, F, M, K, J, L, ? if (videoKeys.indexOf(e.keyCode) !== -1) { e.preventDefault(); e.stopPropagation(); } switch(e.keyCode) { case 32: // SPACE - Play/Pause if (player.paused()) { player.play(); } else { player.pause(); } break; case 37: // LEFT ARROW - Seek backward 10s var currentTime = player.currentTime(); player.currentTime(Math.max(0, currentTime - 10)); showKeyboardTooltip(1000); break; case 39: // RIGHT ARROW - Seek forward 10s var currentTime = player.currentTime(); var duration = player.duration(); player.currentTime(Math.min(duration, currentTime + 10)); showKeyboardTooltip(1000); break; case 38: // UP ARROW - Volume up var volume = Math.min(1, player.volume() + 0.1); player.volume(volume); showVolumeTooltip(volume); break; case 40: // DOWN ARROW - Volume down var volume = Math.max(0, player.volume() - 0.1); player.volume(volume); showVolumeTooltip(volume); break; case 70: // F - Fullscreen toggle if (player.isFullscreen()) { player.exitFullscreen(); } else { player.requestFullscreen(); } break; case 77: // M - Mute toggle player.muted(!player.muted()); break; case 75: // K - Play/Pause (YouTube style) if (player.paused()) { player.play(); } else { player.pause(); } break; case 74: // J - Seek backward 10s (YouTube style) var currentTime = player.currentTime(); player.currentTime(Math.max(0, currentTime - 10)); break; case 76: // L - Seek forward 10s (YouTube style) var currentTime = player.currentTime(); var duration = player.duration(); player.currentTime(Math.min(duration, currentTime + 10)); break; case 191: // ? - Show keyboard shortcuts showKeyboardTooltip(5000); break; } }); // Auto-hide controls on mouse inactivity function hideControlsAfterDelay() { clearTimeout(mouseTimer); mouseTimer = setTimeout(function() { if (!player.paused() && controlsVisible) { playerEl.classList.add("vjs-user-inactive"); controlsVisible = false; } }, 3000); } // Show controls on mouse movement playerEl.addEventListener("mousemove", function(e) { lastMouseMove = Date.now(); if (!controlsVisible) { playerEl.classList.remove("vjs-user-inactive"); controlsVisible = true; } if (!player.paused()) { hideControlsAfterDelay(); } }); // Double-click for fullscreen playerEl.addEventListener("dblclick", function(e) { // Only if clicked on video area, not controls if (e.target === playerEl || e.target.classList.contains("vjs-tech")) { if (player.isFullscreen()) { player.exitFullscreen(); } else { player.requestFullscreen(); } } }); // Click to play/pause (on video area only) playerEl.addEventListener("click", function(e) { // Nếu click vào pause animation layer, đã handle ở createPauseAnimationLayer if (e.target.closest(".vjs-pause-animation-layer")) { return; } // Only if clicked on video area, not controls if (e.target === playerEl || e.target.classList.contains("vjs-tech")) { if (player.paused()) { player.play(); } else { player.pause(); } } }); // Mouse wheel for volume control playerEl.addEventListener("wheel", function(e) { e.preventDefault(); var delta = e.deltaY > 0 ? -0.05 : 0.05; var newVolume = Math.max(0, Math.min(1, player.volume() + delta)); player.volume(newVolume); showVolumeTooltip(newVolume); }); // Focus management playerEl.addEventListener("focus", function() { showKeyboardTooltip(3000); }); // Show keyboard shortcuts on first interaction var hasShownShortcuts = false; playerEl.addEventListener("click", function() { if (!hasShownShortcuts) { setTimeout(function() { showKeyboardTooltip(4000); }, 1000); hasShownShortcuts = true; } }); // Create keyboard tooltip function createKeyboardTooltip() { if (!keyboardTooltip) { keyboardTooltip = document.createElement("div"); keyboardTooltip.className = "vjs-keyboard-tooltip"; keyboardTooltip.innerHTML = "SPACE: Play/Pause | ← →: Seek | ↑ ↓: Volume | F: Fullscreen | M: Mute"; playerEl.appendChild(keyboardTooltip); } return keyboardTooltip; } // Create volume tooltip function createVolumeTooltip() { if (!volumeTooltip) { volumeTooltip = document.createElement("div"); volumeTooltip.className = "vjs-volume-tooltip"; playerEl.appendChild(volumeTooltip); } return volumeTooltip; } // Show keyboard shortcuts tooltip function showKeyboardTooltip(duration) { duration = duration || 2000; var tooltip = createKeyboardTooltip(); tooltip.classList.add("show"); setTimeout(function() { if (tooltip) { tooltip.classList.remove("show"); } }, duration); } // Show volume tooltip function showVolumeTooltip(volume) { var tooltip = createVolumeTooltip(); tooltip.textContent = "Volume: " + Math.round(volume * 100) + "%"; tooltip.classList.add("show"); setTimeout(function() { if (tooltip) { tooltip.classList.remove("show"); } }, 1000); } // Create pause animation layer function createPauseAnimationLayer() { if (!pause_animation_layer && pause_animation_content && pause_animation_content.trim() !== "") { pause_animation_layer = document.createElement("div"); pause_animation_layer.className = "vjs-pause-animation-layer"; var content_wrapper = document.createElement("div"); content_wrapper.className = "vjs-pause-animation-content"; content_wrapper.innerHTML = pause_animation_content; pause_animation_layer.appendChild(content_wrapper); playerEl.appendChild(pause_animation_layer); // Add class để hide default play button playerEl.classList.add("has-pause-animation"); // Auto-play videos trong animation layer var videos = content_wrapper.querySelectorAll("video"); videos.forEach(function(video) { video.autoplay = true; video.muted = true; video.loop = true; video.play().catch(function(e) { console.log("Pause animation video autoplay failed:", e); }); }); // Click để resume video pause_animation_layer.addEventListener("click", function(e) { e.preventDefault(); e.stopPropagation(); if (player.paused()) { player.play(); } }); } return pause_animation_layer; } // Show pause animation function showPauseAnimation() { if (pause_animation_content && pause_animation_content.trim() !== "") { var layer = createPauseAnimationLayer(); if (layer) { layer.classList.add("show"); // Restart videos trong animation var videos = layer.querySelectorAll("video"); videos.forEach(function(video) { video.currentTime = 0; video.play().catch(function(e) { console.log("Restart animation video failed:", e); }); }); } } } // Hide pause animation function hidePauseAnimation() { if (pause_animation_layer) { pause_animation_layer.classList.remove("show"); // Pause videos trong animation để save resources var videos = pause_animation_layer.querySelectorAll("video"); videos.forEach(function(video) { video.pause(); }); } } // Enhanced video visibility với iframe CORS handling function forceVideoVisibility() { try { var videoElement = playerEl.querySelector("video"); var iframeElement = playerEl.querySelector("iframe"); if (videoElement) { videoElement.style.display = "block"; videoElement.style.visibility = "visible"; videoElement.style.opacity = "1"; videoElement.style.background = "transparent"; } if (iframeElement) { iframeElement.style.display = "block"; iframeElement.style.visibility = "visible"; iframeElement.style.opacity = "1"; // CORS-safe iframe modifications try { iframeElement.setAttribute("allow", "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"); iframeElement.setAttribute("allowfullscreen", "true"); iframeElement.setAttribute("referrerpolicy", "strict-origin-when-cross-origin"); // Ensure origin in iframe src var iframeSrc = iframeElement.src; if (iframeSrc && iframeSrc.indexOf("origin=") === -1) { var separator = iframeSrc.indexOf("?") === -1 ? "?" : "&"; var newSrc = iframeSrc + separator + "origin=" + encodeURIComponent(window.location.origin); iframeElement.src = newSrc; } } catch (corsError) { // Ignore CORS errors khi modify iframe } } } catch (e) { // Ignore visibility errors } } // Run visibility check forceVideoVisibility(); // Periodic visibility check với error handling if (is_playlist) { setInterval(function() { try { forceVideoVisibility(); } catch (e) { // Ignore periodic errors } }, 3000); } // Enhanced pause/play event handling player.on("pause", function() { // Show pause animation thay vì play button setTimeout(function() { showPauseAnimation(); }, 100); // Show controls when paused clearTimeout(mouseTimer); playerEl.classList.remove("vjs-user-inactive"); controlsVisible = true; }); player.on("play", function() { // Hide pause animation hidePauseAnimation(); // Start auto-hide when playing hideControlsAfterDelay(); }); player.on("playing", function() { // Ensure animation is hidden when playing hidePauseAnimation(); }); // Handle seeking - hide animation player.on("seeking", function() { hidePauseAnimation(); }); // Tạo thanh trên - THỂ HIỆN XUYÊN SUỐT với fixed positioning var top_bar = document.createElement("div"); top_bar.className = "vjs-top-bar-flex"; var left_group = document.createElement("div"); left_group.className = "vjs-top-bar-left"; left_group.style.cssText = "display: flex; align-items: center; gap: 15px; pointer-events: auto;"; top_bar.appendChild(left_group); // Age warning - Glass gradient effect var age_warning = document.createElement("span"); age_warning.className = "videojs-age-rating-flex"; age_warning.style.cssText = "color: white; font-size: 12px; font-weight: 700; line-height: 1; white-space: nowrap; pointer-events: auto;"; left_group.appendChild(age_warning); var top_title = document.createElement("span"); top_title.className = "vjs-top-title-flex"; top_title.style.cssText = "color: white; font-size: 14px; font-weight: 600; max-width: 400px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"; left_group.appendChild(top_title); var right_group = document.createElement("div"); right_group.className = "vjs-top-bar-right"; right_group.style.cssText = "display: flex; align-items: center; pointer-events: auto;"; top_bar.appendChild(right_group); if (top_logo_url && top_logo_url !== "") { var top_logo = document.createElement("img"); top_logo.src = top_logo_url; top_logo.className = "vjs-top-logo-flex"; top_logo.style.cssText = "max-height: 40px; max-width: 120px; opacity: 0.9; filter: drop-shadow(2px 2px 4px rgba(0,0,0,0.6));"; right_group.appendChild(top_logo); } player.el().appendChild(top_bar); // Tạo thanh dưới - Layout mới: Title trên, Logo dưới, bên trái var bottom_bar = document.createElement("div"); bottom_bar.className = "vjs-bottom-bar-flex"; var bottom_left_group = document.createElement("div"); bottom_left_group.className = "vjs-bottom-bar-left"; bottom_bar.appendChild(bottom_left_group); // Title ở trên var bottom_title = document.createElement("span"); bottom_title.className = "vjs-bottom-title-flex"; bottom_title.style.cssText = "color: white; font-size: 20px; font-weight: 700; max-width: 600px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; z-index: 17; order: 1;"; bottom_left_group.appendChild(bottom_title); // Logo ở dưới if (bottom_logo_url && bottom_logo_url !== "") { var bottom_logo = document.createElement("img"); bottom_logo.src = bottom_logo_url; bottom_logo.className = "vjs-bottom-logo-flex"; bottom_logo.style.cssText = "max-height: 50px; max-width: 150px; opacity: 0.9; filter: drop-shadow(2px 2px 4px rgba(0,0,0,0.6)); z-index: 16; order: 2;"; bottom_left_group.appendChild(bottom_logo); } else { var no_logo_text = document.createElement("span"); no_logo_text.className = "vjs-no-logo-text"; no_logo_text.textContent = "Không Logo"; no_logo_text.style.cssText = "color: rgba(255,255,255,0.6); font-size: 12px; font-style: italic; order: 2;"; bottom_left_group.appendChild(no_logo_text); } player.el().appendChild(bottom_bar); // Age rating logic với animation thu ngắn/kéo dài var age_map = { "P": "Phim dành cho mọi lứa tuổi", "K": "Phim dành cho trẻ em dưới 13 tuổi, khuyến khích xem cùng cha mẹ", "T13": "Phim dành cho khán giả từ 13 tuổi trở lên", "T16": "Phim dành cho khán giả từ 16 tuổi trở lên", "T18": "Phim dành cho khán giả từ 18 tuổi trở lên", "13": "Phim dành cho khán giả từ 13 tuổi trở lên", "16": "Phim dành cho khán giả từ 16 tuổi trở lên", "18": "Phim dành cho khán giả từ 18 tuổi trở lên" }; function get_age_short(code) { code = (code + "").replace(/[^A-Z0-9]/gi, "").toUpperCase(); if (age_map[code]) return code; if (/18/.test(code)) return "T18"; if (/16/.test(code)) return "T16"; if (/13/.test(code)) return "T13"; return "P"; } var age_short = get_age_short(age_code); var age_full = age_map[age_short] || age_map["P"]; var age_timer_running = false; // Hiển thị ban đầu - dạng ngắn age_warning.textContent = age_short; // Function expand/collapse age warning function expandAgeWarning() { age_warning.textContent = age_short + " " + age_full; age_warning.classList.add("expanded"); setTimeout(function() { age_warning.textContent = age_short; age_warning.classList.remove("expanded"); }, 10000); // Hiển thị 10 giây } // Show bottom bar lần đầu sau 5 giây setTimeout(function() { showBottomBar(); }, 5000); // Title animations - Cập nhật nội dung function update_top_title() { if (top_titles.length > 0) { top_title.textContent = top_titles[top_title_index]; top_title_index = (top_title_index + 1) % top_titles.length; } } function update_bottom_title() { if (bottom_titles.length > 0) { bottom_title.textContent = bottom_titles[bottom_title_index]; bottom_title_index = (bottom_title_index + 1) % bottom_titles.length; } } if (top_titles.length > 0) { update_top_title(); setInterval(update_top_title, 5 * 60 * 1000); } if (bottom_titles.length > 0) { update_bottom_title(); setInterval(update_bottom_title, 5 * 60 * 1000); } // Trailer functionality - CHỈ hiện khi trailer đang chạy function createSkipButton() { if (trailer_src && trailer_src !== "" && !skip_button) { skip_button = document.createElement("button"); skip_button.className = "vjs-skip-trailer"; skip_button.textContent = "Bỏ qua Trailer"; skip_button.style.cssText = "position: absolute; top: 60px; right: 15px; z-index: 20; color: white; padding: 12px 24px; border-radius: 12px; cursor: pointer; font-size: 14px; font-weight: 700; transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); pointer-events: auto;"; skip_button.onclick = function() { if (is_trailer_playing) { player.src([{ src: main_src, type: main_type }]); is_trailer_playing = false; trailer_watched = true; hideSkipButton(); player.play(); } }; player.el().appendChild(skip_button); setTimeout(function() { if (skip_button) { skip_button.classList.add("show"); } }, 100); } } function hideSkipButton() { if (skip_button) { skip_button.classList.remove("show"); setTimeout(function() { if (skip_button && skip_button.parentNode) { skip_button.parentNode.removeChild(skip_button); skip_button = null; } }, 400); } } if (is_trailer_playing) { createSkipButton(); } player.on("ended", function() { if (is_trailer_playing && !trailer_watched) { player.src([{ src: main_src, type: main_type }]); is_trailer_playing = false; trailer_watched = true; hideSkipButton(); player.play(); } }); player.on("loadstart", function() { if (!is_trailer_playing) { hideSkipButton(); } }); // Enhanced YTP removal - periodic cleanup function removeYTPElements() { try { var ytpSelectors = [ ".ytp-chrome-top", ".ytp-watermark", ".ytp-chrome-bottom", ".ytp-show-cards-title", ".ytp-ce-element", ".ytp-cards-teaser", ".ytp-gradient-top", ".ytp-gradient-bottom", ".ytp-chrome-controls", ".ytp-title-channel", ".ytp-title", ".ytp-menuitem", ".ytp-popup", ".ytp-contextmenu", ".ytp-settings-menu", ".ytp-panel-menu", ".ytp-tooltip", ".ytp-bezel", ".ytp-info-panel", ".ytp-paid-content-overlay", ".ytp-suggested-action", ".ytp-endscreen-element", ".ytp-impression-link" ]; ytpSelectors.forEach(function(selector) { try { var elements = player.el().querySelectorAll(selector); elements.forEach(function(el) { if (el) { el.style.display = "none"; el.style.visibility = "hidden"; el.style.opacity = "0"; el.style.pointerEvents = "none"; el.style.zIndex = "-9999"; } }); } catch (e) { // Ignore selector errors } }); forceVideoVisibility(); } catch (e) { console.warn("YTP removal error:", e); } } // Run cleanup with better timing setTimeout(removeYTPElements, 100); setInterval(removeYTPElements, 2000); // Run cleanup on video events with error handling player.on("loadstart", function() { try { removeYTPElements(); if (!is_trailer_playing) { hideSkipButton(); } } catch (e) { console.warn("Loadstart event error:", e); } }); player.on("play", function() { try { removeYTPElements(); } catch (e) { console.warn("Play event error:", e); } }); player.on("loadedmetadata", function() { try { removeYTPElements(); } catch (e) { console.warn("Loadedmetadata event error:", e); } }); player.on("canplay", function() { try { removeYTPElements(); } catch (e) { console.warn("Canplay event error:", e); } }); }); // Global error suppression for CORS window.addEventListener("error", function(e) { if (e.message && (e.message.indexOf("CORS") !== -1 || e.message.indexOf("Access-Control-Allow-Origin") !== -1 || e.message.indexOf("innertube") !== -1)) { e.preventDefault(); return false; } }); // Suppress unhandled promise rejections from CORS window.addEventListener("unhandledrejection", function(e) { if (e.reason && e.reason.toString && (e.reason.toString().indexOf("CORS") !== -1 || e.reason.toString().indexOf("innertube") !== -1)) { e.preventDefault(); return false; } }); }); </script>'; return $output; } // CHỈ MỘT LẦN ĐĂNG KÝ SHORTCODE - Generated by An Kun add_shortcode('aks', 'videojs_shortcode'); } add_shortcode( '[aks]', 'videojs_shortcode' );