'', 'download_key' => '', 'stream' => '', 'inline' => '', 'storage' => '', 'remote' => '', 'ssl' => '', 'rewrite' => '', 'rewrite_base' => '', 'skip_confirmation' => '', 'url_to_storage_source' => '', 'count_against_user' => '', 'check_user' => '', 'get_streamer_json' => '', 'get_streamer_array' => ''), $attr); foreach(array_keys(get_defined_vars()) as $__v) $__refs[$__v] =& $$__v; do_action('ws_plugin__s2member_before_sc_get_file_after_shortcode_atts', get_defined_vars()); unset($__refs, $__v); // Housekeeping. $get_streamer_json = filter_var($attr['get_streamer_json'], FILTER_VALIDATE_BOOLEAN); $get_streamer_array = filter_var($attr['get_streamer_array'], FILTER_VALIDATE_BOOLEAN); $get_streamer_json = $get_streamer_array = ($get_streamer_array || $get_streamer_json) ? TRUE : FALSE; foreach($attr as $key => $value) // Now we need to go through and a `file_` prefix to certain Attribute keys, for compatibility. if(strlen($value) && in_array($key, array('download', 'download_key', 'stream', 'inline', 'storage', 'remote', 'ssl', 'rewrite', 'rewrite_base'))) $config['file_'.$key] = $value; // Set prefixed config parameter here so we can pass properly in ``$config`` array. else if(strlen($value) && !in_array($key, array('get_streamer_json', 'get_streamer_array'))) $config[$key] = $value; unset($key, $value); // We don't want these bleeding into Hooks/Filters anyway. if(!empty($config) && isset($config['file_download'])) // Looking for a File Download URL? { $_get = c_ws_plugin__s2member_files::create_file_download_url($config, $get_streamer_array); if($get_streamer_array && $get_streamer_json && is_array($_get)) $get = json_encode($_get); else if($get_streamer_array && $get_streamer_json) $get = 'null'; // Null object value. else if(!empty($_get)) $get = $_get; } return apply_filters('ws_plugin__s2member_sc_get_file', isset($get) ? $get : NULL, get_defined_vars()); } /** * Handles the Shortcode for: `[s2Stream /]`. * * @package s2Member\s2File * @since 130119 * * @attaches-to ``add_shortcode('s2Stream');`` * * @param array $attr An array of Attributes. * @param string $content Content inside the Shortcode. * @param string $shortcode The actual Shortcode name itself. * * @return string HTML markup that produces an audio/video stream for a specific player. */ public static function sc_get_stream($attr = array(), $content = '', $shortcode = '') { foreach(array_keys(get_defined_vars()) as $__v) $__refs[$__v] =& $$__v; do_action('ws_plugin__s2member_before_sc_get_stream', get_defined_vars()); unset($__refs, $__v); // Housekeeping. $attr = c_ws_plugin__s2member_utils_strings::trim_qts_deep((array)$attr); $attr = shortcode_atts(array('download' => '', 'file_download' => '', 'download_key' => '', 'stream' => 'yes', 'inline' => 'yes', 'storage' => '', 'remote' => '', 'ssl' => '', 'rewrite' => 'yes', 'rewrite_base' => '', 'skip_confirmation' => '', 'url_to_storage_source' => 'yes', 'count_against_user' => 'yes', 'check_user' => 'yes', // Configuration 'player' => 'jwplayer-v7-rtmp', 'player_id' => 's2-stream-'.md5(uniqid('', TRUE)), 'player_path' => '/jwplayer/jwplayer.js', 'player_key' => '', 'player_title' => '', 'player_image' => '', 'player_mediaid' => '', 'player_description' => '', 'player_captions' => '', 'player_tracks' => '', 'player_resolutions' => '', // A comma-delimited list of resolution options. // Layout 'player_controls' => 'yes', 'player_skin' => '', 'player_stretching' => 'uniform', 'player_width' => '480', 'player_height' => '270', 'player_aspectratio' => '', // Playback 'player_autostart' => 'no', 'player_fallback' => 'yes', 'player_mute' => 'no', 'player_primary' => (($attr['player'] === 'jw-player-v7' || $attr['player'] === 'jw-player-v6') ? 'html5' : 'flash'), 'player_repeat' => 'no', 'player_startparam' => '', // `startparam` seems to be JW Player v6 only. // Advanced Option Blocks 'player_option_blocks' => ''), $attr); $attr['download'] = (!empty($attr['file_download'])) ? $attr['file_download'] : $attr['download']; foreach(array_keys(get_defined_vars()) as $__v) $__refs[$__v] =& $$__v; do_action('ws_plugin__s2member_before_sc_get_stream_after_shortcode_atts', get_defined_vars()); unset($__refs, $__v); // Housekeeping. //260805 Validate the final player configuration after shortcode hooks, before it affects paths or generated markup. $player_templates = array('jwplayer-v6', 'jwplayer-v6-rtmp', 'jwplayer-v6-rtmp-only', 'jwplayer-v7', 'jwplayer-v7-rtmp', 'jwplayer-v7-rtmp-only'); if(!in_array($attr['player'], $player_templates, TRUE)) $attr['player'] = 'jwplayer-v7-rtmp'; //260805 Shortcode content may select only an exact player script path allowlisted through trusted PHP. $player_paths = array_map('strval', (array)apply_filters('ws_plugin__s2member_sc_get_stream_player_paths', array('/jwplayer/jwplayer.js'), $attr)); $player_paths = array_values(array_unique(array_filter(array_map('trim', $player_paths), 'strlen'))); if(!$player_paths) $player_paths = array('/jwplayer/jwplayer.js'); if(!in_array((string)$attr['player_path'], $player_paths, TRUE)) $attr['player_path'] = $player_paths[0]; //260805 Preserve supported enums and numeric formats; invalid values fall back instead of entering JavaScript syntax. $attr['player_primary'] = strtolower(trim((string)$attr['player_primary'])); if(!in_array($attr['player_primary'], array('html5', 'flash'), TRUE)) $attr['player_primary'] = 'flash'; $attr['player_stretching'] = strtolower(trim((string)$attr['player_stretching'])); if(!in_array($attr['player_stretching'], array('uniform', 'exactfit', 'fill', 'none'), TRUE)) $attr['player_stretching'] = 'uniform'; //260805 Keep JW Player v6 query parameter names as strings; complete output encoding prevents JavaScript-string breakout. $attr['player_startparam'] = trim((string)$attr['player_startparam']); if(!preg_match('/^(?:[0-9]+|[0-9]+(?:\.[0-9]+)?%)$/D', (string)$attr['player_width'])) $attr['player_width'] = '480'; if(!preg_match('/^(?:[0-9]+|[0-9]+(?:\.[0-9]+)?%)$/D', (string)$attr['player_height'])) $attr['player_height'] = '270'; if($attr['player_aspectratio'] && (!preg_match('/^([0-9]+):([0-9]+)$/D', (string)$attr['player_aspectratio'], $_player_aspectratio) || !(integer)$_player_aspectratio[1] || !(integer)$_player_aspectratio[2])) $attr['player_aspectratio'] = ''; unset($_player_aspectratio); //260805 Housekeeping. //260805 Resolution tokens become filename suffixes and labels, so discard tokens outside the supported token format. $_player_resolutions = array(); foreach(preg_split('/[,;\s]+/', (string)$attr['player_resolutions'], -1, PREG_SPLIT_NO_EMPTY) as $_player_resolution) { $_player_resolution = ltrim(trim($_player_resolution), 'Rr'); if($_player_resolution !== '' && preg_match('/^[A-Za-z0-9][A-Za-z0-9_-]*$/D', $_player_resolution)) $_player_resolutions[] = $_player_resolution; } $attr['player_resolutions'] = implode(',', $_player_resolutions); unset($_player_resolutions, $_player_resolution); //260805 Housekeeping. //260805 Encode complete JavaScript string literals once; templates receive generated literals instead of shortcode text. $player_json_strings = array(); foreach(array('player_id', 'player_key', 'player_title', 'player_image', 'player_mediaid', 'player_description', 'player_skin', 'player_aspectratio', 'player_stretching', 'player_primary', 'player_startparam') as $_player_json_string_key) { $_player_json_string = wp_json_encode((string)$attr[$_player_json_string_key], JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); $player_json_strings[$_player_json_string_key] = is_string($_player_json_string) ? $_player_json_string : '""'; } unset($_player_json_string_key, $_player_json_string); //260805 Housekeeping. foreach($attr as $key => $value) // Now we need to go through and a `file_` prefix to certain Attribute keys, for compatibility. if(strlen($value) && in_array($key, array('download', 'download_key', 'stream', 'inline', 'storage', 'remote', 'ssl', 'rewrite', 'rewrite_base'))) $config['file_'.$key] = $value; // Set prefixed config parameter here so we can pass properly in ``$config`` array. else if(strlen($value) && !in_array($key, array('file_download', 'player')) && strpos($key, 'player_') !== 0) $config[$key] = $value; unset($key, $value); // Ditch these now. We don't want these bleeding into Hooks/Filters anyway. if(!empty($config) && isset($config['file_download'])) // Looking for a File Download URL? { if($attr['player_resolutions'] && c_ws_plugin__s2member_utils_conds::pro_is_installed() /* Pro serves SMIL files. */) { $file_download_extension = strtolower(ltrim((string)strrchr(basename($config['file_download']), '.'), '.')); $file_download_resolution_wo_extension = substr($config['file_download'], 0, -(strlen($file_download_extension) + 1) /* For the dot. */); $file_download_wo_resolution_extension = preg_replace('/\-r[0-9]+([^.]*)$/i', '', $file_download_resolution_wo_extension); // e.g., `r720p-HD` is removed here. $file_download_resolutions = array(); // Initialize the array of resolutions. foreach(preg_split('/[,;\s]+/', $attr['player_resolutions'], -1, PREG_SPLIT_NO_EMPTY) as $_player_resolution) { $_player_resolution = ltrim($_player_resolution, 'Rr'); // Remove R|r prefix. $file_download_resolutions[$_player_resolution] = $file_download_wo_resolution_extension.'-r'.$_player_resolution.'.'.$file_download_extension; } unset($_player_resolution); // Housekeeping. $file_download_urls = array(); // Initialize array of all file download urls. foreach($file_download_resolutions as $_player_resolution => $_file_download_resolution) // NOTE: these ARE in a specific order. { $_file_download_config = array_merge($config, array('file_download' => $_file_download_resolution)); if($file_download_urls) // If this is a ANOTHER resolution, don't count it against the user. $_file_download_config = array_merge($_file_download_config, array('check_user' => FALSE, 'count_against_user' => FALSE)); if(!($file_download_urls[str_replace(array('_', '-'), ' ', $_player_resolution)] = c_ws_plugin__s2member_files::create_file_download_url($_file_download_config, TRUE))) return apply_filters('ws_plugin__s2member_sc_get_stream', NULL, get_defined_vars()); // Failure. } unset($_player_resolution, $_file_download_resolution, $_file_download_config); // Housekeeping. } else $file_download_urls = array(c_ws_plugin__s2member_files::create_file_download_url($config, TRUE)); // Default behavior. if($file_download_urls && $attr['player'] && is_file($template = dirname(dirname(__FILE__)).'/templates/players/'.$attr['player'].'.php') && $attr['player_id'] && $attr['player_path']) { $template = (is_file(TEMPLATEPATH.'/'.basename($template))) ? TEMPLATEPATH.'/'.basename($template) : $template; $template = (is_file(get_stylesheet_directory().'/'.basename($template))) ? get_stylesheet_directory().'/'.basename($template) : $template; $template = (is_file(WP_CONTENT_DIR.'/'.basename($template))) ? WP_CONTENT_DIR.'/'.basename($template) : $template; if(strpos($attr['player'], 'jwplayer-v7') === 0) // JW Player (new v7). { $player = trim(c_ws_plugin__s2member_utilities::evl(file_get_contents($template))); $_first_file_download_url = array(); // Holds the first one. $_last_file_download_url = array(); // Holds the last one. $_uses_rtmp_streamers = FALSE; // Streamers use RTMP? $_total_player_sources = count($file_download_urls); // Total sources. $_player_sources_counter = 1; // Player sources counter; needed by the loop below. $player_resolution_aspect_ratio_w = 16; // Default aspect ratio width. $player_resolution_aspect_ratio_h = 9; // Default aspect ratio in height. if($attr['player_aspectratio'] && preg_match('/^[0-9]+\:[0-9]+$/', $attr['player_aspectratio'])) list($player_resolution_aspect_ratio_w, $player_resolution_aspect_ratio_h) = explode(':', $attr['player_aspectratio']); $player_resolution_aspect_ratio_w = (integer)$player_resolution_aspect_ratio_w; // Force integer value. $player_resolution_aspect_ratio_h = (integer)$player_resolution_aspect_ratio_h; // Force integer value. // See: and regarging the SMIL bitrate hints given here. $player_resolution_bitrates = array(2160 => '35000000', 1440 => '10000000', 1080 => '8000000', 720 => '5000000', 640 => '2500001', 480 => '2500000', 360 => '1000000', 320 => '999999', 240 => '500000', 180 => '300000'); $player_resolution_bitrates = apply_filters('ws_plugin__s2member_sc_get_stream_resolution_bitrates', $player_resolution_bitrates, get_defined_vars()); $player_resolution_sources_smil_file_id = md5(serialize($attr).c_ws_plugin__s2member_utils_ip::current()); // Initialize SMIL ID. $player_resolution_sources_smil_file_url = home_url('/s2member-rsf-file.smil?s2member_rsf_file='.urlencode($player_resolution_sources_smil_file_id).'&s2member_rsf_file_ip='.urlencode(c_ws_plugin__s2member_utils_ip::current())); $player_resolution_sources_smil_file_url = c_ws_plugin__s2member_utils_urls::add_s2member_sig($player_resolution_sources_smil_file_url); $player_resolution_sources_smil_file_contents = ''; // Initialize player sources SMIL file contents. $player_sources = array(); //260805 Build source configuration as PHP data before JSON serialization. foreach($file_download_urls as $_file_download_url_label => $_file_download_url) { $_is_first_file_download_url = $_player_sources_counter <= 1; $_is_last_file_download_url = $_player_sources_counter >= $_total_player_sources; if($_is_first_file_download_url) // We base this conditional on the first streamer. $_uses_rtmp_streamers = stripos($_file_download_url['streamer'], 'rtmp') === 0; switch($attr['player'])// See: { case 'jwplayer-v7': // New JW Player v7 (very simple). //260805 Store source fields as data so the JSON encoder controls all JavaScript syntax. $_player_source = array('file' => $_file_download_url['url']); if(is_string($_file_download_url_label)) $_player_source['label'] = $_file_download_url_label; if($_is_first_file_download_url) $_player_source['default'] = 'true'; $player_sources[] = $_player_source; break; // Break switch loop. case 'jwplayer-v7-rtmp': // RTMP w/ downloadable fallback (mobile compatibility). case 'jwplayer-v7-rtmp-only': // RTMP streaming only (flash player only). if($attr['player_resolutions'] && $_total_player_sources > 1 && $_uses_rtmp_streamers) { if($_is_first_file_download_url) // The first source is the SMIL file. { //260805 The generated SMIL URL is serialized as data with the other player sources. $_player_source = array('file' => $player_resolution_sources_smil_file_url); if($_is_first_file_download_url) $_player_source['default'] = 'true'; $player_sources[] = $_player_source; } $_file_download_url['smil']['height'] = (integer)$_file_download_url_label; // e.g., `720p-HD` becomes `720`. if(!$_file_download_url['smil']['height']) $_file_download_url['smil']['height'] = 720; // Use a default height if invalid. $_file_download_url['smil']['width'] = ceil(($_file_download_url['smil']['height'] / $player_resolution_aspect_ratio_h) * $player_resolution_aspect_ratio_w); $_file_download_url['smil']['system-bitrate'] = '1'; // Default value. if(!empty($player_resolution_bitrates[$_file_download_url['smil']['height']])) $_file_download_url['smil']['system-bitrate'] = $player_resolution_bitrates[$_file_download_url['smil']['height']]; $player_resolution_sources_smil_file_contents .= '