This commit is contained in:
2026-04-26 23:47:49 +02:00
parent 1b95f03d1e
commit b073e009d8
5288 changed files with 1112699 additions and 55536 deletions

View File

@@ -0,0 +1,182 @@
<?php
/**
* Class SB_Instagram_API_Connect_Pro
*
* Adds support for additional endpoints:
*
* - Personal account comments
* - Business account top and recent hashtags
* - Business account stories
* - Business account comments
* - Business account hashtag IDs
*
* @since 5.0
*/
namespace SmashBalloon\YouTubeFeed\Pro;
use SmashBalloon\YouTubeFeed\SBY_API_Connect;
class SBY_API_Connect_Pro extends SBY_API_Connect
{
public function connect() {
$url = $this->get_url();
if ( strpos( $url, 'iframe' ) === 0 ) {
$response = $this->get_iframe_response();
$response = json_decode( str_replace( '%22', '&rdquo;', $response ), true );
} else {
$response = wp_remote_get( esc_url_raw( $url ), $this->get_args() );
if ( ! is_wp_error( $response ) ) {
// certain ways of representing the html for double quotes causes errors so replaced here.
$response = json_decode( str_replace( '%22', '&rdquo;', $response['body'] ), true );
}
}
$this->response = $response;
}
private function get_iframe_response() {
return '{"kind":"youtube#playlistItemListResponse","nextPageToken":"single","items":[{"iframe":"'.str_replace( 'iframe_', '', $this->get_url() ).'","id":"blank","snippet":{"publishedAt":"2020-04-01T17:45:02.000Z","channelId":"blank","title":"","description":"","thumbnails":{"default":{"url":"'.trailingslashit( SBY_PLUGIN_URL ) . 'img/placeholder.png'.'","width":120,"height":90},"medium":{"url":"'.trailingslashit( SBY_PLUGIN_URL ) . 'img/placeholder.png'.'","width":320,"height":180},"high":{"url":"'.trailingslashit( SBY_PLUGIN_URL ) . 'img/placeholder.png'.'","width":480,"height":360},"standard":{"url":"'.trailingslashit( SBY_PLUGIN_URL ) . 'img/placeholder.png'.'","width":640,"height":480},"maxres":{"url":"'.trailingslashit( SBY_PLUGIN_URL ) . 'img/placeholder.png'.'","width":1280,"height":720}},"channelTitle":"","playlistId":"UU-blank","position":0,"resourceId":{"kind":"youtube#video","videoId":"iframe"},"contentDetails":{"videoId":"iframe","videoPublishedAt":"2020-04-01T17:45:02.000Z"},"status":{"privacyStatus":"public"}}}]}';
}
public function set_response( $response ) {
$this->response = $response;
}
public function get_next_page( $params = false ) {
if ( $params && isset( $params['video_ids'] ) ) {
if ( isset( $params['nextPageToken'] ) ) {
if ( count( $params['nextPageToken'] ) > SBY_MAX_SINGLE_PAGE ) {
return array_slice( $params['nextPageToken'], SBY_MAX_SINGLE_PAGE );
} else {
return '';
}
} elseif ( count( $params['video_ids'] ) > SBY_MAX_SINGLE_PAGE ) {
return array_slice( $params['video_ids'], SBY_MAX_SINGLE_PAGE );
} else {
return '';
}
} else {
if ( ! empty( $this->response['nextPageToken'] ) ) {
return $this->response['nextPageToken'];
} else {
return '';
}
}
}
/**
* Sets the url for the API request based on the account information,
* type of data needed, and additional parameters
*
* @param $connected_account
* @param $endpoint_slug header or user
* @param $params
*
* @since 1.0
*/
public function set_url( $connected_account, $endpoint_slug, $params = [], $api_key = null ) {
$num = ! empty( $params['num'] ) ? (int)$params['num'] : 50;
$access_credentials = isset( $connected_account['api_key'] ) ? 'key=' . $connected_account['api_key'] : 'access_token=' . $connected_account['access_token'];
$next_page = '';
if ( isset( $params['nextPageToken'] ) && ! is_array( $params['nextPageToken'] ) ) {
$next_page = '&pageToken=' . $params['nextPageToken'];
}
if ( $endpoint_slug === 'tokeninfo' ) {
$url = 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=' . $connected_account['access_token'];
} elseif ( $endpoint_slug === 'channels' ) {
$channel_param = 'mine=true';
if ( isset( $params['channel_name'] ) ) {
$channel_param = 'forUsername=' . $params['channel_name'];
} elseif ( isset( $params['channel_id'] ) ) {
$channel_param = 'id=' . $params['channel_id'];
}
$url = 'https://www.googleapis.com/youtube/v3/channels?part=id,snippet,statistics,contentDetails&'.$channel_param.'&' . $access_credentials . $next_page;
} elseif ( $endpoint_slug === 'live' ) {
$url = 'iframe_'.$params['channelId'];
} elseif ( $endpoint_slug === 'search' ) {
$part = 'snippet';
if ( isset( $params['part'] ) ) {
$part = $this->formatted_part_param_string( $params['part'] );
}
if ( ! isset( $params['isCustom'] ) ) {
if ( isset( $params['eventType'] ) && $params['eventType'] === 'upcoming' ) {
$num = 50; // get max videos so we can reverse sort them to show soonest playing live streams first, default order is by publish date
}
$params_string = $this->formatted_param_string( $params );
} else {
$params_string = $params['customSearch'];
if ( isset( $params['nextPageToken'] ) ) {
$params_string .= '&pageToken=' . $params['nextPageToken'];
}
}
$num = max( 10, $num );
$query_var_string= 'type=video&part='.$part.'&maxResults=' . $num . $params_string;
$url = 'https://www.googleapis.com/youtube/v3/search?'.$query_var_string.'&'.$access_credentials.$next_page;
} elseif ( $endpoint_slug === 'playlistItems' ) {
$url = 'https://www.googleapis.com/youtube/v3/playlistItems?part=id,snippet,contentDetails,status&maxResults='.$num.'&playlistId='.$params['playlist_id'].'&' . $access_credentials.$next_page;
} elseif ( $endpoint_slug === 'single' ) {
$part = 'id,statistics,contentDetails,snippet,liveStreamingDetails';
$vid_ids = empty( $params['nextPageToken'] ) ? $params['video_ids'] : $params['nextPageToken'];
$vid_ids = array_slice( $vid_ids, 0, SBY_MAX_SINGLE_PAGE );
$vid_id_string = implode( ',', $vid_ids );
$url = 'https://www.googleapis.com/youtube/v3/videos?part='.$part.'&id='.$vid_id_string.'&maxResults=50&' . $access_credentials;
} elseif ( $endpoint_slug === 'videos' ) {
$params_string = $this->formatted_param_string( $params );
$part = 'id,statistics,contentDetails';
if ( isset( $params['part'] ) ) {
$part = $this->formatted_part_param_string( $params['part'] );
}
$url = 'https://www.googleapis.com/youtube/v3/videos?part='.$part.$params_string.'&maxResults='.$num.'&' . $access_credentials;
} elseif ( $endpoint_slug === 'videosDuration' ) {
$ids = isset( $params['ids'] ) ? $params['ids'] : [];
$part = 'id,statistics,contentDetails';
$url = 'https://www.googleapis.com/youtube/v3/videos?part='.$part.'&id='.$ids.'&maxResults='.$num.'&' . $access_credentials;
} elseif ( $endpoint_slug === 'comments' ) {
$part = 'snippet,replies';
$video_id = isset( $params['video_id'] ) ? $params['video_id'] : [];
$url = 'https://www.googleapis.com/youtube/v3/commentThreads?part='. $part .'&videoId='.$video_id.'&order=relevance&maxResults='.$num.'&' . $access_credentials;
} elseif ( $endpoint_slug === 'livestream' ) {
$page_token_param = '';
$part = 'id';
$channel_id = isset( $params['channel_id'] ) ? $params['channel_id'] : '';
$event_type = isset( $params['event_type'] ) ? $params['event_type'] : '';
$page_token = isset( $params['page_token'] ) ? $params['page_token'] : '';
if ( !empty($page_token) ) {
$page_token_param = '&pageToken=' . $page_token;
}
$url = 'https://www.googleapis.com/youtube/v3/search?part='. $part .'&channelId='.$channel_id .'&eventType='.$event_type .'&order=date&maxResults=50&type=video&' . $access_credentials . $page_token_param;
} else {
$channel_param = 'mine=true';
if ( isset( $params['username'] ) ) {
$channel_param = 'forUsername=' . $params['username'];
} elseif ( isset( $params['channel_id'] ) ) {
$channel_param = 'id=' . $params['channel_id'];
}
$url = 'https://www.googleapis.com/youtube/v3/channels?part=id,snippet&'.$channel_param.'&' . $access_credentials.$next_page;
}
$this->set_url_from_args( $url );
}
}

View File

@@ -0,0 +1,284 @@
<?php
namespace SmashBalloon\YouTubeFeed\Pro;
class SBY_CPT {
public function __construct() {
add_filter( 'manage_' . SBY_CPT . '_posts_columns', array( $this, 'set_custom_sby_videos_columns' ) );
add_filter( 'manage_edit-' . SBY_CPT . '_sortable_columns', array( $this, 'set_custom_sortable_sby_videos_columns' ), 10, 1 );
add_action( 'manage_' . SBY_CPT . '_posts_custom_column', array( $this, 'custom_sby_videos_column' ), 10, 2 );
add_action( 'pre_get_posts', array( $this, '' . SBY_CPT . '_custom_order' ), 10, 1 );
add_action( 'admin_init', array( $this, 'channel_action_listener' ) );
add_shortcode( 'youtube-feed-single', array( $this, 'youtube_feed_single' ) );
}
public static function set_up_submenus() {
add_submenu_page(
SBY_SLUG,
__( 'Manage Single Videos', 'feeds-for-youtube' ),
__( 'Single Video Settings', 'feeds-for-youtube' ),
'edit_' . SBY_CPT,
'sby_single_settings',
array( __CLASS__, 'single_settings_admin_page' )
);
}
public static function single_settings_admin_page() {
include_once trailingslashit( SBY_PLUGIN_DIR ) . 'inc/Admin/templates/single-settings.php';
}
public static function set_custom_sby_videos_columns( $columns ) {
$columns['channel_title'] = __( 'Channel', 'feeds-for-youtube' );
$columns['video_id'] = __( 'Video ID', 'feeds-for-youtube' );
$columns['youtube_publish_date'] = __( 'Publish Date', 'feeds-for-youtube' );
unset( $columns['author'] );
return $columns;
}
public static function custom_sby_videos_column( $column, $post_id ) {
switch ( $column ) {
case 'channel_title' :
$channel = get_post_meta( $post_id, 'sby_channel_title', true );
if ( ! empty( $channel ) ) {
echo esc_html( $channel );
}
break;
case 'video_id' :
$video_id = get_post_meta( $post_id, 'sby_video_id', true );
if ( ! empty( $video_id ) ) {
echo esc_html( $video_id );
}
break;
case 'youtube_publish_date' :
$publish_date = get_post_meta( $post_id, 'sby_youtube_publish_date', true );
$date_format = get_option( 'date_format' );
$time_format = get_option( 'time_format' );
if ( $date_format && $time_format ) {
$date_time_format = $date_format . ' ' . $time_format;
} else {
$date_time_format = 'F j, Y g:i a';
}
if ( ! empty( $publish_date ) ) {
echo esc_html( date_i18n( $date_time_format, strtotime( $publish_date ) + sby_get_utc_offset() ) );
}
break;
}
}
public static function set_custom_sortable_sby_videos_columns( $columns ) {
$columns['channel_title'] = 'sby_channel_title';
$columns['youtube_publish_date'] = 'sby_youtube_publish_date';
return $columns;
}
public function sby_videos_custom_order( $query ) {
if ( ! is_admin() ) {
return;
}
$orderby = $query->get( 'orderby' );
if ( in_array( $orderby, array( 'sby_channel_title', 'sby_video_id', 'sby_youtube_publish_date' ), true ) ) {
$query->set( 'meta_key', $orderby );
$query->set( 'orderby', 'meta_value' );
}
if ( isset( $_GET['channel_id'] ) && $query->is_main_query() && $query->query_vars['post_type'] == SBY_CPT ) {
//Get original meta query
$meta_query = (array) $query->get( 'meta_query' );
// Add your criteria
$meta_query[] = array(
'key' => 'sby_channel_id',
'value' => sanitize_text_field( $_GET['channel_id'] ),
'compare' => '=',
);
// Set the meta query to the complete, altered query
$query->set( 'meta_query', $meta_query );
}
}
public static function channel_action_listener() {
if ( ! isset( $_GET['sby_action'] ) || ! isset( $_GET['channel'] ) ) {
return;
}
$action = sanitize_text_field( $_GET['sby_action'] );
$channel_id = sanitize_text_field( $_GET['channel'] );
if ( $action === 'publish' ) {
if ( ! empty( $channel_id ) ) {
$args = array(
'channel_id' => $channel_id,
'post_status' => array( 'draft', 'pending' ),
'posts_per_page' => - 1
);
$draft_posts = new SBY_YT_Query( $args );
$draft_posts_arr = $draft_posts->get_posts();
foreach ( $draft_posts_arr as $post ) {
$update_post = array( 'ID' => $post->ID, 'post_status' => 'publish' );
wp_update_post( $update_post );
}
}
} elseif ( $action === 'trash' ) {
if ( ! empty( $channel_id ) ) {
$args = array(
'channel_id' => $channel_id,
'post_status' => 'any',
'posts_per_page' => - 1
);
$draft_posts = new SBY_YT_Query( $args );
$draft_posts_arr = $draft_posts->get_posts();
foreach ( $draft_posts_arr as $post ) {
wp_trash_post( $post->ID );
}
}
}
wp_safe_redirect( admin_url( 'admin.php?page=youtube-feed-single-videos' ) );
}
public static function youtube_feed_single( $atts = array() ) {
$atts = ! empty( $atts ) ? $atts : array();
if ( isset( $atts['postid'] ) ) {
$args = array(
'p' => $atts['postid'],
'post_status' => 'any',
'posts_per_page' => - 1
);
$vid_posts = new SBY_YT_Query( $args );
$vid_posts_arr = $vid_posts->get_posts();
if ( isset ( $vid_posts_arr[0] ) ) {
$youtube_post = $vid_posts_arr[0];
}
} elseif ( isset( $atts['videoid'] ) ) {
$args = array(
'video_id' => $atts['videoid'],
'post_status' => 'any',
'posts_per_page' => - 1
);
$vid_posts = new SBY_YT_Query( $args );
$vid_posts_arr = $vid_posts->get_posts();
if ( isset ( $vid_posts_arr[0] ) ) {
$youtube_post = $vid_posts_arr[0];
}
} else {
global $post;
if ( $post->post_type === SBY_CPT ) {
$youtube_post = $post;
}
}
if ( ! isset( $youtube_post ) ) {
return 'Need to add Post ID';
}
global $sby_settings;
$youtube_post_meta = get_post_meta( $youtube_post->ID );
$api_data = json_decode( $youtube_post_meta['sby_json'][0], true );
$settings = $sby_settings;
$shortcode_atts = wp_json_encode( $atts );
$options_att_arr['cta'] = array(
'type' => 'default'
);
if ( $settings['cta'] === 'link' ) {
$options_att_arr['cta']['type'] = 'link';
}
$options_att_arr['cta']['defaultLink'] = $settings['linkurl'];
$options_att_arr['cta']['defaultText'] = $settings['linktext'];
$options_att_arr['cta']['openType'] = $settings['linkopentype'];
$button_color = str_replace( '#', '', $settings['linkcolor'] );
$button_text_color = str_replace( '#', '', $settings['linktextcolor'] );
$options_att_arr['cta']['color'] = ! empty( $button_color ) ? sby_hextorgb( $button_color ) : '';
$options_att_arr['cta']['textColor'] = ! empty( $button_text_color ) ? sby_hextorgb( $button_text_color ) : '';
if ( ! empty( $settings['descriptionlength'] ) ) {
$options_att_arr['descriptionlength'] = (int)$settings['descriptionlength'];
}
$other_atts = ' data-options="'.esc_attr( wp_json_encode( $options_att_arr ) ).'"';
$icon_type = $settings['font_method'];
wp_enqueue_script( 'sby_scripts' );
include sby_get_feed_template_part( 'shortcode-content', $settings );
}
public static function get_sby_cpt_settings() {
$defaults = array(
'include' => array( 'description', 'stats' ),
'post_status' => 'draft'
);
$sby_videos_settings = get_option( SBY_CPT . '_settings', $defaults );
return $sby_videos_settings;
}
public static function setting_name( $name, $is_array = false ) {
$return = SBY_CPT . '_settings[' . $name . ']';
if ( $is_array ) {
$return .= '[]';
}
return $return;
}
public static function validate_options( $input, $option_name ) {
$updated_options = get_option( $option_name, array() );
foreach ( $input as $key => $val ) {
if ( is_array( $val ) ) {
$updated_options[ $key ] = array();
foreach ( $val as $val2 ) {
$updated_options[ $key ][] = sanitize_text_field( $val2 );
}
} else {
// include in search set to false
if ( $val === 'on' ) {
$val = true;
}
if ( $key === 'search_include' ) {
$updated_options[ $key ] = false;
} else {
$updated_options[ $key ] = sanitize_text_field( $val );
}
}
}
$updated_options = apply_filters( 'sby_single_settings_valid_options', $updated_options, $input );
return $updated_options;
}
}

View File

@@ -0,0 +1,38 @@
<?php
/**
* SBY_Comments_Pro.
*
* @since 2.3.3
*/
namespace SmashBalloon\YouTubeFeed\Pro;
use SmashBalloon\YouTubeFeed\Pro\SBY_Display_Elements_Pro;
class SBY_Comments_Pro
{
/**
* Generates base HTML for displaying comments for list and gallery layout
*
* @return false|string
*
* @since 2.3.3
*/
public static function comment_html($settings) {
$message_icon = '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.6693 13.73L11.0026 11.0633H1.66927C1.3026 11.0633 0.988715 10.9327 0.727604 10.6716C0.466493 10.4105 0.335938 10.0966 0.335938 9.72997V1.72997C0.335938 1.3633 0.466493 1.04941 0.727604 0.788304C0.988715 0.527193 1.3026 0.396637 1.66927 0.396637H12.3359C12.7026 0.396637 13.0165 0.527193 13.2776 0.788304C13.5387 1.04941 13.6693 1.3633 13.6693 1.72997V13.73ZM1.66927 9.72997H11.5693L12.3359 10.48V1.72997H1.66927V9.72997Z" fill="currentColor"/></svg>';
ob_start(); ?>
<div class="sby-comment-container" <?php echo SBY_Display_Elements_Pro::get_comment_data_attributes( $settings ); ?>>
<button class="sby-comments-trigger">
<?php echo $message_icon; ?>
<p><?php echo esc_html( 'Show Comments', 'feeds-for-youtube' ) ?></p>
</button>
<div class="sby-comments-wrap"></div>
</div>
<?php
return ob_get_clean();
}
}

View File

@@ -0,0 +1,233 @@
<?php
/**
* Class SBY_Cron_Updater_Pro
*
* Finds all regular feed transients saved in the database and updates
* each cached feed in the background using WP Cron. This is set up with the
* "sby_cron_updater" function in the if-functions.php file. The "display_instagram"
* function will trigger a single feed update if no transient is found
* for the feed
*
* @since 1.0/1.0
*/
namespace SmashBalloon\YouTubeFeed\Pro;
use SmashBalloon\YouTubeFeed\SBY_Cron_Updater;
use SmashBalloon\YouTubeFeed\Services\AdminAjaxService;
class SBY_Cron_Updater_Pro extends SBY_Cron_Updater
{
/**
* Find and loop through all feed cache transients and update the post and
* header caches
*
* Overwritten in the Pro version
*
* @since 1.0
*/
public static function do_feed_updates() {
$feed_caches = self::get_feed_cache_option_names();
shuffle( $feed_caches );
$database_settings = sby_get_database_settings();
// this is outputted in system info
$report = array(
'notes' => array(
'time_ran' => date( 'Y-m-d H:i:s' ),
'num_found_transients' => count( $feed_caches )
)
);
foreach ( $feed_caches as $feed_cache ) {
$feed_id = str_replace( '_transient_', '', $feed_cache['option_name'] );
$report[ $feed_id ] = array();
$transient = get_transient( $feed_id );
if ( $transient ) {
$feed_data = json_decode( $transient, true );
// shortcode attributes are saved in order to recreate the feed is needed
$atts = isset( $feed_data['atts'] ) ? $feed_data['atts'] : array();
$last_retrieve = isset( $feed_data['last_retrieve'] ) ? (int)$feed_data['last_retrieve'] : 0;
// the last approximate time the feed was requested to be displayed on a page is recorded
// in order to stop updating feeds not in use.
$last_requested = isset( $feed_data['last_requested'] ) ? (int)$feed_data['last_requested'] : false;
$report[ $feed_id ]['last_retrieve'] = date( 'Y-m-d H:i:s', $last_retrieve );
if ( $atts !== false ) {
if ( ! $last_requested || $last_requested > (time() - 60*60*24*30) ) {
$sby_settings_obj = new SBY_Settings_Pro( $atts, $database_settings );
if ( empty( $database_settings['connected_accounts'] ) && empty( $database_settings['api_key'] ) ) {
$report[ $feed_id ]['did_update'] = 'no - no connected account';
} else {
self::do_single_feed_cron_update( $sby_settings_obj, $feed_data, $atts );
$report[ $feed_id ]['did_update'] = 'yes';
}
} else {
$report[ $feed_id ]['did_update'] = 'no - not recently requested';
}
} else {
$report[ $feed_id ]['did_update'] = 'no - missing atts';
}
} else {
$report[ $feed_id ]['did_update'] = 'no - no transient found';
}
}
update_option( 'sby_cron_report', $report, false );
}
/**
* Update a single feed cache based on settings. Local image storing and
* resizing is done in the background here as well unless this is the initial
* time the feed is created and no cached data exists yet.
*
* Overwritten in the Pro version
*
* @param object $sby_settings_obj object created by the sby_settings class
* @param array $feed_data post, header, shortcode settings, and other info
* associated with the feed that is saved in the cache
* @param array $atts shortcode settings
* @param bool $include_resize whether or not to resize images during the update since
* images can also be resized with an ajax call when the feed is viewed on the frontend
*
* @since 1.0
*/
public static function do_single_feed_cron_update( $sby_settings_obj, $feed_data, $atts, $include_resize = true ) {
$sby_settings_obj->set_feed_type_and_terms();
$sby_settings_obj->set_transient_name();
$transient_name = $sby_settings_obj->get_transient_name();
$settings = $sby_settings_obj->get_settings();
$feed_type_and_terms = $sby_settings_obj->get_feed_type_and_terms();
$youtube_feed = new SBY_Feed_Pro( $transient_name );
while ( $youtube_feed->need_posts( $settings['num'] ) && $youtube_feed->can_get_more_posts() ) {
$youtube_feed->add_remote_posts( $settings, $feed_type_and_terms, $sby_settings_obj->get_connected_accounts_in_feed() );
}
$to_cache = array(
'atts' => $atts,
'last_requested' => $feed_data['last_requested'],
'last_retrieve' => time()
);
$youtube_feed->set_cron_cache( $to_cache, $sby_settings_obj->get_cache_time_in_seconds(), $settings['backup_cache_enabled'] );
if ( $youtube_feed->need_header( $settings, $feed_type_and_terms ) ) {
$youtube_feed->set_remote_header_data( $settings, $feed_type_and_terms, $sby_settings_obj->get_connected_accounts_in_feed() );
$youtube_feed->cache_header_data( $sby_settings_obj->get_cache_time_in_seconds(), $settings['backup_cache_enabled'] );
}
$post_data = $youtube_feed->get_post_data();
$post_data = array_slice( $post_data, 0, $settings['num'] );
AdminAjaxService::sby_process_post_set_caching( $post_data, $transient_name );
}
/**
* Retrieve option name column values for all feed cache transients
*
* @return array
*
* @since 1.0
*/
public static function get_feed_cache_option_names() {
global $wpdb;
$feed_caches = array();
$results = $wpdb->get_results( "
SELECT option_name
FROM $wpdb->options
WHERE `option_name` LIKE ('%\_transient\_sby\_%')
AND `option_name` NOT LIKE ('%\_transient\_sby\_-%')
AND `option_name` NOT LIKE ('%\_transient\_sby\_header%');", ARRAY_A );
if ( isset( $results[0] ) ) {
$feed_caches = $results;
}
return $feed_caches;
}
/**
* Deletes comment transient on feed schedule.
*
* @return void
*
* @since 2.3.3
*/
public static function do_comment_delete() {
global $wpdb;
$database_settings = sby_get_database_settings();
if ( empty( $database_settings['connected_accounts'] ) && empty( $database_settings['api_key'] ) ) {
// Define the prefix for the transients you want to delete
$transient_prefix = 'sby_comment_';
// Prepare the SQL query to delete transients with the specified prefix
$sql = $wpdb->prepare(
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
$wpdb->esc_like($transient_prefix) . '%'
);
// Execute the query
$wpdb->query($sql);
}
}
/**
* Start cron jobs based on user's settings for cron cache update frequency.
* This is triggered when settings are saved on the "Configure" tab.
*
* @param string $sby_cache_cron_interval arbitrary name from one of the
* settings on the "Configure" tab
* @param string $sby_cache_cron_time hour of the day (1 = 1:00)
* @param string $sby_cache_cron_am_pm am or pm (time of day)
*
* @since 1.0
*/
public static function start_cron_job( $sby_cache_cron_interval, $sby_cache_cron_time, $sby_cache_cron_am_pm ) {
wp_clear_scheduled_hook( 'sby_feed_update' );
if ( $sby_cache_cron_interval === '12hours' || $sby_cache_cron_interval === '24hours' ) {
$relative_time_now = time() + sby_get_utc_offset();
$base_day = strtotime( date( 'Y-m-d', $relative_time_now ) );
$add_time = $sby_cache_cron_am_pm === 'pm' ? (int)$sby_cache_cron_time + 12 : (int)$sby_cache_cron_time;
$utc_start_time = $base_day + (($add_time * 60 * 60) - sby_get_utc_offset());
if ( $utc_start_time < time() ) {
if ( $sby_cache_cron_interval === '12hours' ) {
$utc_start_time += 60*60*12;
} else {
$utc_start_time += 60*60*24;
}
}
if ( $sby_cache_cron_interval === '12hours' ) {
wp_schedule_event( $utc_start_time, 'twicedaily', 'sby_feed_update' );
} else {
wp_schedule_event( $utc_start_time, 'daily', 'sby_feed_update' );
}
} else {
if ( $sby_cache_cron_interval === '30mins' ) {
wp_schedule_event( time(), 'sby30mins', 'sby_feed_update' );
} else {
wp_schedule_event( time(), 'hourly', 'sby_feed_update' );
}
}
}
}

View File

@@ -0,0 +1,240 @@
<?php
/**
* Class SBY_Display_Elements_Pro
*
* @since 5.0
*/
namespace SmashBalloon\YouTubeFeed\Pro;
use SmashBalloon\YouTubeFeed\SBY_Display_Elements;
class SBY_Display_Elements_Pro extends SBY_Display_Elements
{
/**
* @param $type
* @param $icon_type
* @param $styles
*
* @return string
*
* @since 5.0
*/
private static function get_pro_icons( $type, $icon_type, $styles = '' ) {
if ( $type === 'date' ) {
if ( $icon_type === 'svg' ) {
return '<svg ' . $styles . ' class="svg-inline--fa fa-clock fa-w-16" aria-hidden="true" data-fa-processed="" data-prefix="far" data-icon="clock" role="presentation" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm61.8-104.4l-84.9-61.7c-3.1-2.3-4.9-5.9-4.9-9.7V116c0-6.6 5.4-12 12-12h32c6.6 0 12 5.4 12 12v141.7l66.8 48.6c5.4 3.9 6.5 11.4 2.6 16.8L334.6 349c-3.9 5.3-11.4 6.5-16.8 2.6z"></path></svg>';
} else {
return '<i class="fa fa-clock" aria-hidden="true"></i>';
}
} elseif ( $type === 'likes' ) {
if ( $icon_type === 'svg' ) {
return '<svg ' . $styles . ' class="svg-inline--fa fa-heart fa-w-18" aria-hidden="true" data-fa-processed="" data-prefix="fa" data-icon="heart" role="presentation" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M414.9 24C361.8 24 312 65.7 288 89.3 264 65.7 214.2 24 161.1 24 70.3 24 16 76.9 16 165.5c0 72.6 66.8 133.3 69.2 135.4l187 180.8c8.8 8.5 22.8 8.5 31.6 0l186.7-180.2c2.7-2.7 69.5-63.5 69.5-136C560 76.9 505.7 24 414.9 24z"></path></svg>';
} else {
return '<i class="fa fa-heart" aria-hidden="true"></i>';
}
} elseif ( $type === 'comments' ) {
if ( $icon_type === 'svg' ) {
return '<svg ' . $styles . ' class="svg-inline--fa fa-comment fa-w-18" aria-hidden="true" data-fa-processed="" data-prefix="fa" data-icon="comment" role="presentation" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M576 240c0 115-129 208-288 208-48.3 0-93.9-8.6-133.9-23.8-40.3 31.2-89.8 50.3-142.4 55.7-5.2.6-10.2-2.8-11.5-7.7-1.3-5 2.7-8.1 6.6-11.8 19.3-18.4 42.7-32.8 51.9-94.6C21.9 330.9 0 287.3 0 240 0 125.1 129 32 288 32s288 93.1 288 208z"></path></svg>';
} else {
return '<i class="fa fa-comment" aria-hidden="true"></i>';
}
} elseif ( $type === 'newlogo' ) {
return '<svg ' . $styles . ' class="sby_new_logo fa-instagram fa-w-14" aria-hidden="true" data-fa-processed="" data-prefix="fab" data-icon="instagram" role="img" viewBox="0 0 448 512">
<path fill="currentColor" d="M224.1 141c-63.6 0-114.9 51.3-114.9 114.9s51.3 114.9 114.9 114.9S339 319.5 339 255.9 287.7 141 224.1 141zm0 189.6c-41.1 0-74.7-33.5-74.7-74.7s33.5-74.7 74.7-74.7 74.7 33.5 74.7 74.7-33.6 74.7-74.7 74.7zm146.4-194.3c0 14.9-12 26.8-26.8 26.8-14.9 0-26.8-12-26.8-26.8s12-26.8 26.8-26.8 26.8 12 26.8 26.8zm76.1 27.2c-1.7-35.9-9.9-67.7-36.2-93.9-26.2-26.2-58-34.4-93.9-36.2-37-2.1-147.9-2.1-184.9 0-35.8 1.7-67.6 9.9-93.9 36.1s-34.4 58-36.2 93.9c-2.1 37-2.1 147.9 0 184.9 1.7 35.9 9.9 67.7 36.2 93.9s58 34.4 93.9 36.2c37 2.1 147.9 2.1 184.9 0 35.9-1.7 67.7-9.9 93.9-36.2 26.2-26.2 34.4-58 36.2-93.9 2.1-37 2.1-147.8 0-184.8zM398.8 388c-7.8 19.6-22.9 34.7-42.6 42.6-29.5 11.7-99.5 9-132.1 9s-102.7 2.6-132.1-9c-19.6-7.8-34.7-22.9-42.6-42.6-11.7-29.5-9-99.5-9-132.1s-2.6-102.7 9-132.1c7.8-19.6 22.9-34.7 42.6-42.6 29.5-11.7 99.5-9 132.1-9s102.7-2.6 132.1 9c19.6 7.8 34.7 22.9 42.6 42.6 11.7 29.5 9 99.5 9 132.1s2.7 102.7-9 132.1z"></path>
</svg>';
//return '<i class="sby_new_logo" aria-hidden="true"></i>';
} elseif ( $type === 'map_marker' ) {
if ( $icon_type === 'svg' ) {
return '<svg ' . $styles . ' class="svg-inline--fa fa-map-marker fa-w-12" aria-hidden="true" data-fa-processed="" data-prefix="fa" data-icon="map-marker" role="presentation" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M172.268 501.67C26.97 291.031 0 269.413 0 192 0 85.961 85.961 0 192 0s192 85.961 192 192c0 77.413-26.97 99.031-172.268 309.67-9.535 13.774-29.93 13.773-39.464 0z"></path></svg>';
} else {
return '<i class="fa fa-map-marker" aria-hidden="true"></i>';
}
} elseif ( $type === 'photo' ) {
if ( $icon_type === 'svg' ) {
return '<svg class="svg-inline--fa fa-image fa-w-16" aria-hidden="true" data-fa-processed="" data-prefix="far" data-icon="image" role="presentation" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M464 448H48c-26.51 0-48-21.49-48-48V112c0-26.51 21.49-48 48-48h416c26.51 0 48 21.49 48 48v288c0 26.51-21.49 48-48 48zM112 120c-30.928 0-56 25.072-56 56s25.072 56 56 56 56-25.072 56-56-25.072-56-56-56zM64 384h384V272l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L208 320l-55.515-55.515c-4.686-4.686-12.284-4.686-16.971 0L64 336v48z"></path></svg>';
} else {
return '<i class="fa fa-image" aria-hidden="true"></i>';
}
} elseif ( $type === 'user' ) {
if ( $icon_type === 'svg' ) {
return '<svg class="svg-inline--fa fa-user fa-w-16" style="margin-right: 3px;" aria-hidden="true" data-fa-processed="" data-prefix="fa" data-icon="user" role="presentation" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M96 160C96 71.634 167.635 0 256 0s160 71.634 160 160-71.635 160-160 160S96 248.366 96 160zm304 192h-28.556c-71.006 42.713-159.912 42.695-230.888 0H112C50.144 352 0 402.144 0 464v24c0 13.255 10.745 24 24 24h464c13.255 0 24-10.745 24-24v-24c0-61.856-50.144-112-112-112z"></path></svg>';
} else {
return '<i class="fa fa-user" aria-hidden="true"></i>';
}
} elseif ( $type === 'views' ) {
if ( $icon_type === 'svg' ) {
return '<svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="eye" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" class="svg-inline--fa fa-eye fa-w-18"><path fill="currentColor" d="M288 144a110.94 110.94 0 0 0-31.24 5 55.4 55.4 0 0 1 7.24 27 56 56 0 0 1-56 56 55.4 55.4 0 0 1-27-7.24A111.71 111.71 0 1 0 288 144zm284.52 97.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400c-98.65 0-189.09-55-237.93-144C98.91 167 189.34 112 288 112s189.09 55 237.93 144C477.1 345 386.66 400 288 400z" class=""></path></svg>';
} else {
return '<i class="fa fa-eye" aria-hidden="true"></i>';
}
} else {
return '';
}
}
/**
* A not very elegant but useful method to abstract out how the settings
* work for displaying certain elements in the feed.
*
* @param string $element specific key, view below for supported ones
* @param $settings
*
* @return bool
*
* @since 5.0
*/
public static function should_show_element( $element, $context, $settings ) {
if ( sby_doing_customizer( $settings ) ) {
return true;
}
//user, views, date
if ( $context === 'item-hover' ) {
if ( $settings['layout'] === 'list' ) {
$include_array = array();
} else {
$include_array = $settings['hoverinclude'];
}
} elseif ( $context === 'result' ) {
$include_array = array();
} elseif ( $context === 'single' ) {
$cpt_settings = SBY_CPT::get_sby_cpt_settings();
$include_array = $cpt_settings['include'];
} else {
$include_array = $settings['include'];
}
$include_array = is_array( $include_array ) ? $include_array : explode( ',', str_replace( ' ', '', $include_array ) );
if ( $element === 'meta' ) {
if ( in_array( 'user', $include_array, true )
|| in_array( 'views', $include_array, true )
|| in_array( 'date', $include_array, true ) ) {
return true;
} else {
return false;
}
} elseif ( in_array( $element, $include_array, true ) ) {
return true;
}
return false;
}
/**
* Not used with the core feed but can be used for customizations.
*
* @param $settings
*
* @return string
*
* @since 5.0
*/
public static function get_feed_type_class( $settings ) {
return 'sby_feed_type_' . esc_attr( $settings['type'] );
}
/**
* Boxed style headers have more color options - primary color
*
* @param $settings
*
* @return string
*
* @since 5.0
*/
public static function get_boxed_header_styles( $settings ) {
if ( ! empty( $settings['headerprimarycolor'] ) ) {
return 'style="background: rgb(' . esc_attr( sby_hextorgb( $settings['headerprimarycolor'] ) ). ');"';
}
return '';
}
/**
* Boxed style headers have more color options - secondary color
*
* @param $settings
*
* @return string
*
* @since 5.0
*/
public static function get_header_bar_styles( $settings ) {
if ( ! empty( $settings['headersecondarycolor'] ) ) {
return 'style="background: rgb(' . esc_attr( sby_hextorgb( $settings['headersecondarycolor'] ) ). ');"';
}
return '';
}
/**
* For text, likes counts, post counts
*
* @param $settings
*
* @return string
*
* @since 5.0
*/
public static function get_header_info_styles( $settings ) {
if ( ! empty( $settings['headerprimarycolor'] ) ) {
return 'style="color: rgb(' . esc_attr( sby_hextorgb( $settings['headerprimarycolor'] ) ). ');"';
}
return '';
}
/**
* Layout for mobile feeds altered with the class added here based on settings.
*
* @param $settings
*
* @return string
*
* @since 5.0
*/
public static function get_mobilecols_class( $settings ) {
$disable_mobile = $settings['disablemobile'];
( $disable_mobile == 'on' || $disable_mobile == 'true' || $disable_mobile == true ) ? $disable_mobile = true : $disable_mobile = false;
if( $settings[ 'disablemobile' ] === 'false' ) $disable_mobile = '';
if ( $disable_mobile !== ' sby_disable_mobile' && $settings['colsmobile'] !== 'same' ) {
$colsmobile = (int)( $settings['colsmobile'] ) > 0 ? (int)$settings['colsmobile'] : 'auto';
return ' sby_mob_col_' . $colsmobile;
} else {
$colsmobile = (int)( $settings['cols'] ) > 0 ? (int)$settings['cols'] : 4;
return ' sby_disable_mobile sby_mob_col_' . (int)$settings['cols'];
}
}
public static function get_list_type_subscribe_bar_attr( $settings ) {
$customizer = sby_doing_customizer( $settings );
if ( ! $customizer ) {
return;
}
return self::create_condition_show_vue( $customizer, '$parent.valueIsEnabled($parent.customizerFeedData.settings.layout == \'list\' && $parent.customizerFeedData.settings.enablesubscriberlink)');
}
/**
* Comment data attributes
*
* @param array $settings
*
* @return string
*
* @since 2.3.3
*/
public static function get_comment_data_attributes( $settings ) {
if ( ! sby_doing_customizer( $settings ) ) {
return '';
}
return ' ' . self::display_vue_condition( 'enablecomments' );
}
}

View File

@@ -0,0 +1,665 @@
<?php
/**
* Class SBY_Feed_Pro
*
* The Pro class mostly adds additional methods
* used in the "display_instagram" function for supporting
* additional features.
*
* @since 1.0
*/
namespace SmashBalloon\YouTubeFeed\Pro;
use SmashBalloon\YouTubeFeed\SBY_API_Connect;
use SmashBalloon\YouTubeFeed\SBY_Feed;
use SmashBalloon\YouTubeFeed\SBY_Parse;
class SBY_Feed_Pro extends SBY_Feed
{
private $stats_cache;
public function set_next_pages( $next_pages ) {
$this->next_pages = $next_pages;
}
public function need_header( $settings, $feed_types_and_terms ) {
if ( ! empty( $settings['headerchannel'] ) || isset( $feed_types_and_terms['channels'] ) ) {
return true;
}
return false;
}
public function get_first_user( $feed_types_and_terms, $settings = array() ) {
if ( ! is_array( $settings ) ) {
return '';
}
if ( ! empty( $settings['headerchannel'] ) ) {
return $settings['headerchannel'];
} elseif ( isset( $feed_types_and_terms['channels'][0] ) ) {
return $feed_types_and_terms['channels'][0]['term'];
} else {
return '';
}
}
/**
* Uses the settings to determine if avatars are going to be used.
* Can make feed creation faster if not.
*
* @param $settings
*
* @return bool
*
* @since 2.0
*/
public function need_avatars( $settings ) {
if ( isset( $settings['type'] ) && $settings['type'] === 'hashtag' ) {
return false;
} elseif ( isset( $settings['disablelightbox'] ) && ($settings['disablelightbox'] === 'true' || $settings['disablelightbox'] === 'on') ) {
return false;
} else {
return true;
}
}
/**
* Available avatars are added to the feed as an attribute so they can be used in the lightbox
*
* @param $connected_accounts_in_feed
* @param $feed_types_and_terms
*
* @since 2.0
*/
public function set_up_feed_avatars( $connected_accounts_in_feed, $feed_types_and_terms ) {
foreach ( $feed_types_and_terms as $type => $terms ) {
foreach ( $terms as $term_and_params ) {
$existing_channel_cache = $this->get_channel_cache( $term_and_params['term'], true );
$avatar = SBY_Parse::get_avatar( $existing_channel_cache );
$channel_id = SBY_Parse::get_channel_id( $existing_channel_cache );
$this->set_avatar( $channel_id, $avatar );
}
}
}
/**
* Creates a key value pair of the username and the url of
* the avatar image
*
* @param $name
* @param $url
*
* @since 2.0
*/
public function set_avatar( $channel_id, $url ) {
$this->channel_id_avatars[ $channel_id ] = $url;
}
/**
* @return array
*/
public function get_channel_id_avatars() {
return $this->channel_id_avatars;
}
/**
* the API_Connect class can use either a premade url or
* settings from a connected account, type, and parameters
*
* @param array|string $connected_account_or_page
* @param null $type
* @param null $params
*
* @return object
*/
public function make_api_connection( $connected_account_or_page, $type = NULL, $params = NULL ) {
return new SBY_API_Connect_Pro( $connected_account_or_page, $type, $params );
}
public function is_efficient_type( $type ) {
return in_array( $type, array( 'playlist', 'channel' ), true );
}
public function get_play_list_for_term( $type, $term, $connected_account_for_term, $params ) {
if ( $type === 'playlist' ) {
return $term;
}
if ( $type === 'search' || $type === 'single' || $type === 'live' ) {
return false;
}
$existing_channel_cache = $this->get_channel_cache( $term );
if ( $existing_channel_cache ) {
$this->channels_data[ $term ] = $existing_channel_cache;
}
if ( empty( $this->channels_data[ $term ] ) ) {
if ( $connected_account_for_term['expires'] < time() + 5 ) {
$error_message = '<p><b>' . __( 'Reconnect to YouTube to show this feed.', 'feeds-for-youtube' ) . '</b></p>';
$error_message .= '<p>' . __( 'To create a new feed, first connect to YouTube using the "Connect to YouTube to Create a Feed" button on the settings page and connect any account.', 'feeds-for-youtube' ) . '</p>';
if ( current_user_can( 'manage_youtube_feed_options' ) ) {
$error_message .= '<a href="' . admin_url( 'admin.php?page=youtube-feed-settings' ) . '" target="blank" rel="noopener nofollow">' . __( 'Reconnect in the YouTube Feeds Settings Area', 'feeds-for-youtube' ) . '</a>';
}
global $sby_posts_manager;
$sby_posts_manager->add_frontend_error( 'accesstoken', $error_message );
$sby_posts_manager->add_error( 'accesstoken', array( 'Trying to connect a new account', $error_message ) );
return false;
}
$channel_data = array();
$api_connect_channels = $this->make_api_connection( $connected_account_for_term, 'channels', $params );
$this->add_report( 'channel api call made for ' . $term . ' - ' . $type );
$api_connect_channels->connect();
if ( ! $api_connect_channels->is_wp_error() && ! $api_connect_channels->is_youtube_error() ) {
$channel_data = $api_connect_channels->get_data();
$channel_id = SBY_Parse::get_channel_id( $channel_data );
$this->set_channel_cache( $channel_id, $channel_data );
if ( isset( $params['channel_name'] ) ) {
sby_set_channel_id_from_channel_name( $params['channel_name'], $channel_id );
$this->set_channel_cache( $params['channel_name'], $channel_data );
}
$params = array( 'channel_id' => $channel_id );
$this->channels_data[ $channel_id ] = $channel_data;
$this->channels_data[ $term ] = $channel_data;
} else {
if ( ! $api_connect_channels->is_wp_error() ) {
$return = SBY_API_Connect::handle_youtube_error( $api_connect_channels->get_data(), $connected_account_for_term );
if ( $return && isset( $return['access_token'] ) ) {
$connected_account_for_term['access_token'] = $return['access_token'];
$connected_accounts_for_feed[ $term ]['access_token'] = $return['access_token'];
$connected_account_for_term['expires'] = $return['expires_in'] + time();
$connected_accounts_for_feed[ $term ]['expires'] = $return['expires_in'] + time();
sby_update_or_connect_account( $connected_account_for_term );
$this->add_report( 'refreshing access token for ' . $connected_account_for_term['channel_id'] );
$sby_api_connect_channel = $this->make_api_connection( $connected_account_for_term, 'channels', $params );
$sby_api_connect_channel->connect();
if ( ! $sby_api_connect_channel->is_youtube_error() ) {
$channel_data = $sby_api_connect_channel->get_data();
$channel_id = SBY_Parse::get_channel_id( $channel_data );
$this->set_channel_cache( $channel_id, $channel_data );
if ( isset( $params['channel_name'] ) ) {
sby_set_channel_id_from_channel_name( $params['channel_name'], $channel_id );
$this->set_channel_cache( $params['channel_name'], $channel_data );
}
$this->channels_data[ $channel_id ] = $channel_data;
$this->channels_data[ $term ] = $channel_data;
}
} else {
$this->add_report( 'error connecting to channel' );
}
} else {
$api_connect_channels->handle_wp_remote_get_error( $api_connect_channels->get_data() );
}
}
}
if ( $type === 'favorites' ) {
$playlist = isset( $this->channels_data[ $term ]['items'][0]['contentDetails']['relatedPlaylists']['favorites'] ) ? $this->channels_data[ $term ]['items'][0]['contentDetails']['relatedPlaylists']['favorites'] : false;
if ( $playlist === false ) {
$this->add_report( 'No favorites playlist found' );
}
} else {
$playlist = isset( $this->channels_data[ $term ]['items'][0]['contentDetails']['relatedPlaylists']['uploads'] ) ? $this->channels_data[ $term ]['items'][0]['contentDetails']['relatedPlaylists']['uploads'] : false;
}
return $playlist;
}
protected function sort_posts( $post_set, $settings ) {
if ( empty( $post_set ) ) {
return $post_set;
}
// sorting done with "merge_posts" to be more efficient
if ( $settings['sortby'] === 'alternate' || $settings['sortby'] === 'relevance' || $settings['sortby'] === 'api' ) {
$return_post_set = $post_set;
} elseif ( $settings['sortby'] === 'random' ) {
/*
* randomly selects posts in a random order. Cache saves posts
* in this random order so paginating does not cause some posts to show up
* twice or not at all
*/
usort( $post_set, 'sby_rand_sort' );
$return_post_set = $post_set;
} else {
$scheduled_start = SBY_Parse_Pro::get_scheduled_start_timestamp( $post_set[0] );
if ( ! empty( $scheduled_start ) ) {
usort($post_set, 'sby_scheduled_start_sort' );
} else {
// compares posted on dates of posts
usort( $post_set, 'sby_date_sort' );
}
$return_post_set = $post_set;
}
return $return_post_set;
}
/**
* Used for filtering a single API request worth of posts
*
* @param $post_set
*
* @return mixed
*
* @since 5.0
* @since 5.1 support for filtering "includes any includeword
* and also does not include any excludeword"
*/
protected function filter_posts( $post_set, $settings = array() ) {
$hide_upcoming = $settings['type'] === 'live' && ! $settings['showpast'];
if ( empty( $settings['includewords'] )
&& empty( $settings['excludewords'] )
&& empty( $settings['whitelist'] )
&& empty( $settings['hidevideos'] )
&& empty( $hide_upcoming) ) {
return $post_set;
}
$includewords = ! empty( $settings['includewords'] ) ? explode( ',', $settings['includewords'] ) : array();
$excludewords = ! empty( $settings['excludewords'] ) ? explode( ',', $settings['excludewords'] ) : array();
$hide_videos = ! empty( $settings['hidevideos'] ) && empty( $settings['doingModerationMode'] ) ? explode( ',', str_replace( ' ', '', $settings['hidevideos'] ) ) : array();
$white_list = ! empty( $settings['whitelist'] ) && empty( $settings['doingModerationMode'] ) ? get_option( 'sb_youtube_white_lists_'.$settings['whitelist'], array() ) : false;
$filtered_posts = array();
foreach ( $post_set as $post ) {
$keep_post = false;
$padded_caption = ' ' . str_replace( array( '+', '%0A' ), ' ', urlencode( str_replace( '#', ' HASHTAG', strtolower( SBY_Parse_Pro::get_caption( $post ) ) ) ) ) . ' ';
$padded_title = ' ' . str_replace( array( '+', '%0A' ), ' ', urlencode( str_replace( '#', ' HASHTAG', strtolower( SBY_Parse_Pro::get_video_title( $post ) ) ) ) ) . ' ';
$id = SBY_Parse_Pro::get_video_id( $post );
$post_id = SBY_Parse_Pro::get_post_id( $post );
$is_hidden = false;
if ( ! empty( $hide_videos )
&& ((in_array( $id, $hide_videos, true ) || in_array( 'sby_' . $id, $hide_videos, true )) || (in_array( $post_id, $hide_videos, true ) || in_array( 'sby_' . $post_id, $hide_videos, true ))) ) {
$is_hidden = true;
}
// any blocked photos will not pass any additional filters so don't bother processing
if ( ! $is_hidden ) {
$is_on_white_list = false;
$passes_word_filter = true;
if ( $white_list ) {
if ( in_array( $id, $white_list, true ) || in_array( 'sby_' . $id, $white_list, true ) ) {
$is_on_white_list = true;
}
} elseif ( ! empty( $includewords ) || ! empty( $excludewords ) ) {
$has_includeword = $this->has_filter_keyword( $includewords, $padded_caption, $padded_title );
$has_excludeword = $this->has_filter_keyword( $excludewords, $padded_caption, $padded_title );
if ( ! empty( $includewords ) ) {
$passes_word_filter = $has_includeword;
}
if ( ! empty( $has_excludeword ) ) {
$passes_word_filter = ! $has_excludeword;
}
} else {
// no other filters so it belongs in the feed
$keep_post = true;
}
if ( $is_on_white_list || $passes_word_filter ) {
$keep_post = true;
}
if ( $hide_upcoming ) {
$actual_end_timestamp_a = SBY_Parse_Pro::get_actual_end_timestamp( $post ); // get the time it ended
if ( $actual_end_timestamp_a > 0 ) { // started but hasn't ended! show it first, it's streaming now
$keep_post = false;
}
}
}
$keep_post = apply_filters( 'sby_passes_filter', $keep_post, $post, $settings );
if ( $keep_post ) {
$filtered_posts[] = $post;
}
}
return $filtered_posts;
}
private function has_filter_keyword( $keywords, $padded_caption, $padded_title ) {
$has_keyword = false;
if ( ! empty( $keywords ) ) {
foreach ( $keywords as $keyword ) {
if ( ! empty( $keyword ) ) {
$converted_keyword = trim( str_replace( '+', ' ',
urlencode( str_replace( '#', 'HASHTAG', strtolower( $keyword ) ) ) ) );
if ( preg_match( '/\b' . $converted_keyword . '\b/i', $padded_caption, $matches ) ) {
$has_keyword = true;
} elseif ( preg_match( '/\b' . $converted_keyword . '\b/i', $padded_title, $matches ) ) {
$has_keyword = true;
}
}
}
}
return $has_keyword;
}
/**
* Total number of IDs in the white list already exist in the feed. Used
* to prevent further pagination when no more white listed posts will be
* found
*
* @param array $settings
* @param int $offset
*
* @return bool
*
* @since 5.0
*/
protected function xfeed_is_complete( $settings, $offset = 0 ) {
if ( ! empty( $settings['whitelist_ids'] ) ) {
if ( isset( $settings['doingModerationMode'] ) && $settings['doingModerationMode'] ) {
return false;
}
$total_posts_loaded = $settings['num'] + $offset;
if ( (int)$settings['whitelist_num'] <= $total_posts_loaded ) {
return true;
}
}
return false;
}
/**
* Adds various data attributes to the main feed divthat are used
* by the JavaScript file to layout the feed, trigger certain features,
* and launchvmoderation mode
*
* @param $other_atts
* @param $settings
*
* @return string
*
* @since 5.0
*/
protected function add_other_atts( $other_atts, $settings ) {
$options_att_arr = array();
$customizer = sby_doing_customizer( $settings );
$layout = $settings['layout'];
if ( ! in_array( $layout, array( 'masonry', 'highlight', 'carousel' ) ) ) {
$layout = 'grid';
}
if ( $layout === 'carousel' ) {
$arrows = $settings['carouselarrows'] == 'true' || $settings['carouselarrows'] == 'on' || $settings['carouselarrows'] == 1 || $settings['carouselarrows'] == '1';
$pag = $settings['carouselpag'] == 'true' || $settings['carouselpag'] == 'on' || $settings['carouselpag'] == 1 || $settings['carouselpag'] == '1';
$autoplay = $settings['carouselautoplay'] == 'true' || $settings['carouselautoplay'] == 'on' || $settings['carouselautoplay'] == 1 || $settings['carouselautoplay'] == '1';
$time = $autoplay ? (int)$settings['carouseltime'] : false;
$loop = ! empty( $settings['carouselloop'] ) && ($settings['carouselloop'] !== 'rewind') ? false : true;
$rows = ! empty( $settings['carouselrows'] ) ? min( (int)$settings['carouselrows'], 2 ) : 1;
$options_att_arr['carousel'] = array( $arrows, $pag, $autoplay, $time, $loop, $rows );
}
$options_att_arr['cta'] = array(
'type' => 'default'
);
if ( $settings['cta'] === 'link' ) {
$options_att_arr['cta']['type'] = 'link';
} else if ( $settings['cta'] === 'related' ) {
$options_att_arr['cta']['type'] = 'related';
$options_att_arr['cta']['defaultPosts'] = array();
if ( $settings['num'] < 5 ) {
$options_att_arr['cta']['defaultPosts'] = $this->get_cta_posts();
}
}
$options_att_arr['cta']['defaultLink'] = $settings['linkurl'];
$options_att_arr['cta']['defaultText'] = $settings['linktext'];
$options_att_arr['cta']['openType'] = $settings['linkopentype'];
$button_color = str_replace( '#', '', $settings['linkcolor'] );
$button_text_color = str_replace( '#', '', $settings['linktextcolor'] );
$options_att_arr['cta']['color'] = ! empty( $button_color ) ? sby_hextorgb( $button_color ) : '';
$options_att_arr['cta']['textColor'] = ! empty( $button_text_color ) ? sby_hextorgb( $button_text_color ) : '';
if ( ! empty( $settings['descriptionlength'] ) ) {
$options_att_arr['descriptionlength'] = (int)$settings['descriptionlength'];
}
$other_atts .= ' data-options="'.esc_attr( wp_json_encode( $options_att_arr ) ).'"';
return $other_atts;
}
public function get_cta_posts() {
$posts = $this->get_post_data();
if ( count( $posts ) >= 4 ) {
$cta_posts_indices = array_rand( $posts, min( count( $posts ), 5 ) );
$cta_array = array();
foreach ( $cta_posts_indices as $cta_post_index ) {
$cta_array[] = array(
'videoID' => SBY_Parse::get_video_id( $posts[ $cta_post_index ] ),
'thumbnail' => SBY_Parse::get_media_url( $posts[ $cta_post_index ], 'medium' ),
'title' => SBY_Parse::get_video_title( $posts[ $cta_post_index ] )
);
}
} else {
$cta_array = array();
foreach ( $posts as $post ) {
$cta_array[] = array(
'videoID' => SBY_Parse::get_video_id( $post ),
'thumbnail' => SBY_Parse::get_media_url( $post, 'medium' ),
'title' => SBY_Parse::get_video_title( $post )
);
}
}
return $cta_array;
}
public function requires_workaround_connection( $type ) {
return $type === 'live';
}
public function make_workaround_connection( $connected_account_for_term, $type, $params, $feed_id = '' ) {
$live_streams = new SBY_Live_Streams( $params['channelId'], $feed_id );
$new_live_streams = $live_streams->add_remote_posts();
$live_streams->sort();
$live_streams->update_cached_video_details( $new_live_streams );
$live_streams->update_cache();
$videos = $live_streams->get_video_cache();
$response = array(
'items' => array_values( $videos )
);
$connection = new SBY_API_Connect_Pro( $connected_account_for_term, $type, $params );
$connection->set_response( $response );
return $connection;
}
/**
* Creates an array of standard classes to be added to the main feed div.
*
* @param $settings
*
* @return array
*
* @since 5.0
*/
protected function xadd_classes( $settings ) {
$classes = array();
$moderation_mode = (isset ( $_GET['sbi_moderation_mode'] ) && $_GET['sbi_moderation_mode'] === 'true' && current_user_can( 'edit_posts' ));
if ( $moderation_mode ) {
$classes[] = 'sbi_moderation_mode';
}
return array();
}
public function convert_feed_id_to_stats_transient( $feed_id ) {
$attempt = str_replace( 'sby_', 'sby_-', $feed_id );
if ( $attempt === $feed_id ) {
$attempt = '-' . $attempt;
}
return $attempt;
}
public function get_misc_data( $feed_id, $posts ) {
$misc = array(
'stats' => array()
);
if ( ! empty( $feed_id ) ) {
$stats_cache = $this->get_regular_stats_cache( $feed_id );
if ( $stats_cache ) {
$this->add_report('Found stats cache');
} else {
$this->add_report('No stats cache found');
}
$misc['stats'] = $stats_cache ? $stats_cache : array();
}
if ( ! empty( $posts ) ) {
$vid_ids = array();
foreach ( $posts as $post ) {
$vid_ids[] = SBY_Parse::get_video_id( $post );
}
if ( ! empty( $vid_ids ) ) {
$details_query = new SBY_YT_Details_Query( array( 'video_ids' => $vid_ids ) );
$stats_cached_results = $details_query->get_cached_details_for_posts();
$this->add_report('Getting cached stats for posts');
$organized_stats = array();
$organized_live_streaming_details = array();
foreach ( $stats_cached_results as $post ) {
$organized_stats[ $post['sby_video_id'] ] = $post;
$organized_live_streaming_details[ $post['sby_video_id'] ] = $post;
}
$misc['stats'] = $organized_stats;
$misc['live_streaming_details'] = $organized_live_streaming_details;
if ( ! empty( $feed_id ) ) {
$this->add_report('Adding to stats cache');
$this->add_stats_to_cache( $feed_id, $misc['stats'] );
}
}
}
return $misc;
}
protected function add_stats_to_cache( $feed_id, $stats ) {
$stats_array = is_array( $stats ) ? $stats : array();
$cache = $this->get_regular_stats_cache( $feed_id );
if ( is_array( $cache ) ) {
$stats_array = array_merge( $stats_array, $cache );
}
$stats_json = wp_json_encode( $stats_array );
set_transient( $this->convert_feed_id_to_stats_transient( $feed_id ), $stats_json );
$this->set_stats_data( $stats_array );
}
/**
* @return array
*
* @since 1.0
*/
public function get_stats_data() {
return $this->stats_cache;
}
/**
* @return array
*
* @since 1.0
*/
public function set_stats_data( $stats_array ) {
$this->stats_cache = $stats_array;
}
protected function posts_loop( $posts, $settings, $offset = 0 ) {
$header_data = $this->get_header_data();
$image_ids = array();
$post_index = $offset;
if ( ! isset( $settings['feed_id'] ) ) {
$settings['feed_id'] = $this->regular_feed_transient_name;
}
$misc_data = $this->get_misc_data( $settings['feed_id'], $posts );
$icon_type = $settings['font_method'];
foreach ( $posts as $post ) {
$post_id = SBY_Parse::get_post_id( $post );
$video_id = SBY_Parse::get_video_id( $post );
$image_ids[] = $post_id;
if ( empty( $misc_data['stats'][ $video_id ] ) ) {
$this->post_ids_with_no_details[] = $video_id;
}
include sby_get_feed_template_part( 'item', $settings );
$post_index++;
}
$this->image_ids_post_set = $image_ids;
}
/**
* Checks the database option related the transient expiration
* to ensure it will be available when the page loads
*
* @return bool
*
* @since 2.0/4.0
*/
public function get_regular_stats_cache( $feed_id ) {
//Check whether the cache transient exists in the database and is available for more than one more minute
$transient = get_transient( $this->convert_feed_id_to_stats_transient( $feed_id ) );
if ( $transient ) {
$transient = json_decode( $transient );
}
return $transient;
}
}

View File

@@ -0,0 +1,364 @@
<?php
/**
* SBY_Live_Streams.
*
* Live streamed videos are stored in a cache due to there being no
* reliable way to retrieve then using an API key and a standard API endpoint.
* The RSS feed for the channel is used to get the latest 15 published videos
* and this class can be used tp store all of the ones that are scheduled
* live streams.
*
* @since 1.3
*/
namespace SmashBalloon\YouTubeFeed\Pro;
use SmashBalloon\YouTubeFeed\SBY_Parse;
use SmashBalloon\YouTubeFeed\SBY_RSS_Connect;
class SBY_Live_Streams {
/**
* @var string
*/
private $channel;
/**
* @var int
*/
private $feed_id;
/**
* @var string
*/
private $cache_name;
/**
* @var array
*/
private $video_cache;
/**
* SBY_Live_Streams constructor.
*
* @param $channel string
*
* @since 1.3
*/
public function __construct( $channel, $feed_id ) {
$this->channel = $channel;
$this->feed_id = $feed_id;
$this->cache_name = 'sby_livestreams_' . $channel;
$this->video_cache = get_option( $this->cache_name, array() );
}
/**
* Cached video api data
*
* @return array
*
* @since 1.3
*/
public function get_video_cache() {
return $this->video_cache;
}
/**
* Get latest 15 videos from RSS and return only
* live streams. Use the video IDs to retrieve details
* using the "single" video API endpoint. Update or add
* to the cache based on results.
*
* @return array
*
* @since 1.3
*/
public function add_remote_posts() {
$api_videos = $this->fetch_via_api();
if(empty($api_videos)) {
$api_videos = $this->fetch_rss();
}
$live_streams = $this->fetch_singles( $api_videos );
$this->update_or_add( $live_streams );
return $live_streams;
}
/**
* Refresh the API data for the most recent 40
* videos in the cache. Can exclude videos if they
* were just updated from another process.
*
* @param $exclude array
*
* @since 1.3
*/
public function update_cached_video_details( $exclude ) {
$to_check = array_slice( $this->video_cache, 0, 40 + count( $exclude ) );
$to_update = array();
foreach ( $to_check as $vid_id => $post ) {
// $exclude set as an associative array with the video ID
// as the index.
if ( ! isset( $exclude[ $vid_id ] )
&& count( $to_update ) < 40 ) {
$to_update[] = $vid_id;
}
}
if ( ! empty( $to_update ) ) {
$live_streams = $this->fetch_singles( $to_update );
$this->update_or_add( $live_streams, $to_update );
}
}
/**
* The RSS feed contains live stream videos.
*
* @return array
*
* @since 1.3
*/
public function fetch_rss() {
$params = array(
'channel_id' => $this->channel,
'livestream' => '1'
);
$connection = new SBY_RSS_Connect( 'playlistItems', $params );
$connection->connect();
$data = $connection->get_data();
$ids = array();
if ( is_array( $data ) ) {
foreach ( $data as $post ) {
$vid_id = SBY_Parse::get_video_id( $post );
$ids[] = $vid_id;
}
}
return $ids;
}
/**
* Retrieves the video IDs of live stream feeds for a specific event type.
*
* @param string $event_type The type of event for which to fetch live stream video IDs.
* This parameter determines the category or type of live streams to query.
* @param string $page_token (Optional) The token to retrieve the next page of results, if available.
* Default is an empty string, which fetches the first page.
*
* @return array An associative array containing the video IDs of the live streams and any
*
* @since 1.3
*
*/
public function get_live_stream_feed_video_ids($event_type, $page_token = '') {
$ids = [];
$page_token = '';
$params = array(
'channel_id' => $this->channel,
'event_type' => $event_type,
);
if( !empty($page_token) ) {
$params['page_token'] = $page_token;
}
$connection = new SBY_API_Connect_Pro(sby_get_first_connected_account(), 'livestream', $params);
$connection->connect();
if ( ! $connection->is_youtube_error() ) {
$items = !empty( $connection->get_data()['items'] ) ? $connection->get_data()['items'] : '';
$page_token = !empty( $connection->get_data()['nextPageToken'] ) ? $connection->get_data()['nextPageToken'] : '';
if( !empty($items)) {
foreach ( $items as $single ) {
$videoId = isset($single['id']['videoId']) ? $single['id']['videoId'] : '';
$ids[] = $videoId;
}
}
}
return [
'ids' => $ids,
'page_token' => $page_token
];
}
/**
* Get livestream feed using api.
*
* @return array
*
* @since 1.3
*/
public function fetch_via_api() {
$atts = ['feed' => (int)$this->feed_id];
$database_settings = sby_get_database_settings();
$youtube_feed_settings = new SBY_Settings_Pro($atts, $database_settings);
if (empty($database_settings['connected_accounts']) && empty($database_settings['api_key'])) {
wp_send_json_error('Error: No connected account');
}
$settings = $youtube_feed_settings->get_settings();
$data = [];
$event_types = ['upcoming','live'];
$page_token = '';
$archive_page_count = !empty($settings['showpast'] )? apply_filters( 'sby_past_live_stream_num_pages', 1 ) : 0;
// For adding more past live streams in the future.
if ($archive_page_count > 0) {
$event_types[] = 'completed';
}
foreach ($event_types as $single) {
$page_token = ''; // Initialize the page token for each event type.
$current_page = 0; // Track the current page for "completed" events.
do {
// Fetch data with or without a page token.
if ('completed' === $single && !empty($page_token)) {
$response = self::get_live_stream_feed_video_ids($single, $page_token);
} else {
$response = self::get_live_stream_feed_video_ids($single);
}
if (!empty($response)) {
// Add video IDs to the data array.
$ids = !empty($response['ids']) ? $response['ids'] : [];
array_push($data, ...$ids);
// Update the page token for the next loop iteration.
$page_token = !empty($response['page_token']) ? $response['page_token'] : '';
// Increment the page counter for "completed" events.
if ('completed' === $single) {
$current_page++;
}
} else {
// Exit the loop if no response or no more pages.
$page_token = '';
}
} while (!empty($page_token) && $current_page < $archive_page_count); // Limit pages based on $archive_page_count.
}
return $data;
}
/**
* Divides an array of items into smaller batches of a specified size.
*
* @param array $items The array of items to be divided into batches.
* @param int $batchSize The maximum number of items per batch.
* @return array An array of batches, with each batch being a sub-array of items.
*/
public function slice_into_batches($items, $batchSize) {
return array_chunk($items, $batchSize);
}
/**
* Uses the "single" API endpoint to get video details and
* returns only those that appear to be live streams.
*
* @param $vid_ids array
*
* @return array
*
* @since 1.3
*/
public function fetch_singles( $vid_ids ) {
if( empty($vid_ids) ) {
return [];
}
$batches = self::slice_into_batches($vid_ids, SBY_MAX_SINGLE_PAGE);
$live_streamed_videos = array();
foreach ($batches as $batch) {
$params['video_ids'] = $batch;
$connected_account = sby_get_first_connected_account();
$video_connection = new SBY_API_Connect_Pro( $connected_account, 'single', $params );
$video_connection->connect();
$potential_live_streams = $video_connection->get_data();
if ( is_array( $potential_live_streams['items'] ) ) {
foreach ( $potential_live_streams['items'] as $post ) {
if ( ! empty( $post['liveStreamingDetails']['scheduledStartTime'] )
|| ! empty( $post['liveStreamingDetails']['actualStartTime'] ) ) {
$vid_id = SBY_Parse::get_video_id( $post );
$live_streamed_videos[ $vid_id ] = $post;
}
}
}
}
return $live_streamed_videos;
}
/**
* Updates or adds to video cache stored as an associative array with
* the video ID as the index.
*
* @param $live_streams array
* @param $update_attempted array
*
* @since 1.3
*/
public function update_or_add( $live_streams, $update_attempted = array() ) {
$actually_retrieved = array();
foreach ( $live_streams as $vid_id => $live_stream ) {
$actually_retrieved[] = $vid_id;
$this->video_cache[ $vid_id ] = $live_stream;
}
// If a video ID is attempted to be retrieve but nothing is returned for it
// It's likely that it was removed so we should remove it from the cache
$no_longer_exist = array_diff( $update_attempted, $actually_retrieved );
if ( ! empty( $no_longer_exist ) ) {
foreach ( $no_longer_exist as $vid_id ) {
if ( isset( $this->video_cache[ $vid_id ] ) ) {
unset( $this->video_cache[ $vid_id ] );
}
}
}
}
/**
* Orders videos by schedules start date or actual start date
* when available starting with the most recent.
*
* @since 1.3
*/
public function sort() {
$post_set = $this->video_cache;
uasort($post_set, 'sby_scheduled_start_sort' );
$this->video_cache = $post_set;
}
/**
* Save most recent 200 videos in cache.
*
* @since 1.3
*/
public function update_cache() {
$to_cache = array_slice( $this->video_cache, 0, 200 );
update_option( $this->cache_name, $to_cache, false );
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* Class SBY_Parse_Pro
*
* @since 5.0
*/
namespace SmashBalloon\YouTubeFeed\Pro;
use SmashBalloon\YouTubeFeed\SBY_Parse;
class SBY_Parse_Pro extends SBY_Parse {
public static function get_item_avatar( $post, $avatars = array() ) {
if ( empty ( $avatars ) ) {
return '';
} else {
$username = SBY_Parse_Pro::get_channel_id( $post );
if ( isset( $avatars[ $username ] ) ) {
return $avatars[ $username ];
}
}
return '';
}
/**
* Number of posts made by account
*
* @param $header_data
*
* @return int
*
* @since 5.0
*/
public static function get_post_count( $header_data ) {
if ( isset( $header_data['data']['counts'] ) ) {
return $header_data['data']['counts']['media'];
} elseif ( isset( $header_data['counts'] ) ) {
return $header_data['counts']['media'];
} elseif ( isset( $header_data['media_count'] ) ) {
return $header_data['media_count'];
}
return 0;
}
/**
* Number of followers for account
*
* @param $header_data
*
* @return int
*
* @since 5.0
*/
public static function get_follower_count( $header_data ) {
if ( isset( $header_data['data']['counts'] ) ) {
return $header_data['data']['counts']['followed_by'];
} elseif ( isset( $header_data['counts'] ) ) {
return $header_data['counts']['followed_by'];
} elseif ( isset( $header_data['followers_count'] ) ) {
return $header_data['followers_count'];
}
return 0;
}
public static function get_actual_end_timestamp( $post, $misc_data = array() ) {
if ( ! empty( $post['liveStreamingDetails']['actualEndTime'] ) ) {
$remove_extra = str_replace( array( 'T', '+00:00', '.000Z', '+' ), ' ', $post['liveStreamingDetails']['actualEndTime'] );
$timestamp = strtotime( $remove_extra );
return $timestamp;
} elseif ( isset( $misc_data['live_streaming_details'][ SBY_Parse::get_video_id( $post ) ]['sby_actual_end_time'] ) ) {
return strtotime( $misc_data['live_streaming_details'][ SBY_Parse::get_video_id( $post ) ]['sby_actual_end_time'] );
} elseif ( isset( $misc_data['sby_actual_end_time'][0] ) ) {
return strtotime( $misc_data['sby_actual_end_time'][0] );
} elseif ( isset( $post['sby_actual_end_time'] ) ) {
return strtotime( $post['sby_actual_end_time'] );
}
return 0;
}
/**
* Get video duration from post
*
* YouTube provide video duration in ISO 8601 format so we need to convert it to duration
*
* @since 2.1
*/
public static function get_video_duration( $post ) {
if ( !isset( $post['contentDetails']['duration'] ) && !isset( $post['snippet']['videoDuration'] ) ) {
return;
}
$duration = isset( $post['contentDetails']['duration'] ) ? $post['contentDetails']['duration'] : $post['snippet']['videoDuration'];
$interval = new \DateInterval($duration);
$totalSeconds = $interval->s + $interval->i * 60 + $interval->h * 3600;
$hours = floor($totalSeconds / 3600);
$minutes = floor(($totalSeconds - $hours * 3600) / 60);
$seconds = str_pad($totalSeconds % 60, 2, '0', STR_PAD_LEFT);
if ( $hours > 0 ) {
return "$hours:$minutes:$seconds";
} else {
return "$minutes:$seconds";
}
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace SmashBalloon\YouTubeFeed\Pro;
class SBY_Search
{
public function __construct() {
add_shortcode( SBY_SEARCH_SLUG, array( $this, 'youtube_video_search' ) );
}
public static function youtube_video_search( $atts ) {
global $sby_settings;
$atts = is_array( $atts ) ? $atts : array();
$input_name = SBY_SEARCH_NAME;
$search_term = '';
$results = array();
$results_title = array();
$results_description = array();
if ( isset( $_GET[ $input_name ] ) ) {
$search_term = sanitize_text_field( $_GET[ $input_name ] );
$args = array(
's' => $search_term,
);
$title_query = new SBY_YT_Query( $args );
$results_title = $title_query->get_posts();
if ( empty( $results_title ) ) {
$args = array(
'sbys' => $search_term,
'sbys_in' => array( 'description' )
);
$description_query = new SBY_YT_Query( $args );
$results_description = $description_query->get_posts();
}
wp_reset_postdata();
$results_raw = array_merge( $results_title, $results_description );
$results = SBY_Search::remove_duplicates( $results_raw );
}
ob_start();
include sby_get_feed_template_part( 'form', $sby_settings );
$html = ob_get_contents();
ob_get_clean();
return $html;
}
public static function remove_duplicates( $posts ) {
$return_posts = array();
$ids_included = array();
foreach ( $posts as $post ) {
if ( ! in_array( $post->ID, $ids_included, true ) ) {
$ids_included[] = $post->ID;
$return_posts[] = $post;
}
}
return $return_posts;
}
public static function results_loop( $posts ) {
global $sby_settings;
foreach ( $posts as $youtube_post ) {
$youtube_post_meta = get_post_meta( $youtube_post->ID );
$api_data = json_decode( $youtube_post_meta['sby_json'][0], true );
$settings = $sby_settings;
include sby_get_feed_template_part( 'result', $sby_settings );
}
}
}

View File

@@ -0,0 +1,508 @@
<?php
namespace SmashBalloon\YouTubeFeed\Pro;
use SmashBalloon\YouTubeFeed\SBY_Settings;
class SBY_Settings_Pro extends SBY_Settings {
protected function after_settings_set() {
if ( $this->settings['type'] === 'search'
&& $this->settings['sortby'] === 'none' ) {
$this->settings['sortby'] = 'relevance';
}
}
/**
* Based on the settings related to retrieving post data from the API,
* this setting is used to make sure all endpoints needed for the feed are
* connected and stored for easily looping through when adding posts
*
* Overwritten in the Pro version.
*
* @since 1.0
*/
public function set_feed_type_and_terms() {
//global $sby_posts_manager;
$connected_accounts_in_feed = array();
$feed_type_and_terms = array();
$connected_account = sby_get_first_connected_account();
$channel_only = isset( $connected_account['api_key'] ) ? false : true;
if ( $channel_only ) {
$updated_hoverinclude = array();
if ( \is_array( $this->settings['hoverinclude'] ) ) {
foreach ( $this->settings['hoverinclude'] as $hoverinclude ) {
if ( ! in_array( $hoverinclude, array( 'views', 'stats', 'countdown' ), true ) ) {
$updated_hoverinclude[] = $hoverinclude;
}
}
$this->settings['hoverinclude'] = $updated_hoverinclude;
}
if ( is_array( $this->settings['include'] ) ) {
$updated_include = array();
foreach ( $this->settings['include'] as $include ) {
if ( ! in_array( $include, array( 'views', 'stats', 'countdown' ), true ) ) {
$updated_include[] = $include;
}
}
$this->settings['include'] = $updated_include;
}
global $sby_posts_manager;
$message = '<p><b>' . __( 'Important: No API Key Entered.', 'feeds-for-youtube' ). '</b>';
$message .= '<p>'. __( 'Many features are not available without adding an API Key. Please go to the YouTube Feeds settings page to add an API key after following <a href="https://smashballoon.com/youtube-api-key/" target="_blank" rel="noopener">these instructions.</a>', 'feeds-for-youtube' ). '</p>';
$sby_posts_manager->add_frontend_error( 'no_api_key', $message );
}
if ( ! $channel_only && $this->settings['type'] === 'search' ) {
$feed_type_and_terms = array( 'search' => array() );
if ( $this->settings['usecustomsearch'] ) {
$searches_array = is_array( $this->settings['customsearch'] ) ? $this->settings['customsearch'] : explode( ',', $this->settings['customsearch'] );
foreach ( $searches_array as $search ) {
$search_slug = trim( preg_replace( "/[^A-Za-z0-9]/", '', $search ) );
$feed_type_and_terms['search'][] = array(
'term' => $search_slug,
'params' => array(
'isCustom' => true,
'customSearch' => $this->settings['customsearch'],
'type' => 'video'
)
);
$connected_accounts_in_feed[ $search_slug ] = $connected_account;
}
} elseif ( ! empty( $this->settings['search'] ) ) {
$searches_array = is_array( $this->settings['search'] ) ? $this->settings['search'] : explode( ',', $this->settings['search'] );
foreach ( $searches_array as $search ) {
$search_slug = urlencode( trim( $search ) );
$feed_type_and_terms['search'][] = array(
'term' => $search_slug,
'params' => array(
'q' => trim( $search ),
'type' => 'video'
)
);
$connected_accounts_in_feed[ $search_slug ] = $connected_account;
}
}
} elseif ( ! $channel_only && $this->settings['type'] === 'live' ) {
$feed_type_and_terms = array( 'live' => array() );
$raw_term_att = '';
if ( ! empty( $this->settings['live'] ) ) {
$raw_term_att = $this->settings['live'];
} elseif ( ! empty( $this->settings['id'] ) ) {
$raw_term_att = $this->settings['id'];
} elseif ( ! empty( $this->settings['channel'] ) ) {
$raw_term_att = $this->settings['channel'];
}
if ( ! empty( $raw_term_att ) ) {
$channels_array = is_array( $raw_term_att ) ? $raw_term_att : explode( ',', $raw_term_att );
if ( empty( $this->settings['headerchannel'] ) ) {
$this->settings['headerchannel'] = $channels_array[0];
}
foreach ( $channels_array as $channel ) {
$feed_type_and_terms['live'][] = array(
'term' => $channel.'_live',
'params' => array(
'channelId' => trim( $channel ),
'eventType' => 'live',
'type' => 'video'
)
);
$connected_accounts_in_feed[ $channel.'_live' ] = $connected_account;
}
}
} elseif ( ! $channel_only && $this->settings['type'] === 'playlist' ) {
$this->settings['sortby'] = 'api';
$feed_type_and_terms = array( 'playlist' => array() );
$raw_term_att = '';
if ( ! empty( $this->settings['playlist'] ) ) {
$raw_term_att = $this->settings['playlist'];
} elseif ( ! empty( $this->settings['id'] ) ) {
$raw_term_att = $this->settings['id'];
}
if ( ! empty( $raw_term_att ) ) {
$playlist_array = is_array( $raw_term_att ) ? $raw_term_att : explode( ',', $raw_term_att );
foreach ( $playlist_array as $playlist ) {
$feed_type_and_terms['playlist'][] = array(
'term' => $playlist,
'params' => array(
'playlistId' => $playlist,
)
);
$connected_accounts_in_feed[ $playlist ] = $connected_account;
}
}
} elseif ( ! $channel_only && $this->settings['type'] === 'favorites' ) {
$feed_type_and_terms = array( 'favorites' => array() );
if ( ! empty( $this->settings['id'] ) ) {
$channel_array = is_array( $this->settings['id'] ) ? $this->settings['id'] : explode( ',', str_replace( ' ', '', $this->settings['id'] ) );
if ( empty( $this->settings['headerchannel'] ) ) {
$this->settings['headerchannel'] = $channel_array[0];
}
foreach ( $channel_array as $channel ) {
if ( isset( $this->connected_accounts[ $channel ] ) ) {
$feed_type_and_terms['favorites'][] = array(
'term' => $this->connected_accounts[ $channel ]['channel_id'],
'params' => array(
'channel_id' => $this->connected_accounts[ $channel ]['channel_id']
)
);
$connected_accounts_in_feed[ $this->connected_accounts[ $channel ]['channel_id'] ] = $this->connected_accounts[ $channel ];
}
}
if ( empty( $connected_accounts_in_feed ) ) {
$an_account = array();
foreach ( $this->connected_accounts as $account ) {
if ( empty( $an_account ) ) {
$an_account = $account;
}
}
foreach ( $channel_array as $channel ) {
$feed_type_and_terms['channels'][] = array(
'term' => $channel,
'params' => array(
'channel_id' => $channel
)
);
$connected_accounts_in_feed[ $channel ] = $an_account;
}
}
} elseif ( ! $channel_only && ! empty( $this->settings['favorites'] ) ) {
$channel_array = is_array( $this->settings['favorites'] ) ? $this->settings['favorites'] : explode( ',', str_replace( ' ', '', $this->settings['favorites'] ) );
$an_account = array();
foreach ( $this->connected_accounts as $account ) {
if ( empty( $an_account ) ) {
$an_account = $account;
}
}
if ( empty( $this->settings['headerchannel'] ) ) {
$this->settings['headerchannel'] = $channel_array[0];
}
foreach ( $channel_array as $channel ) {
if ( strpos( $channel, 'UC' ) !== 0 ) {
$channel_id = sby_get_channel_id_from_channel_name( $channel );
if ( $channel_id ) {
$feed_type_and_terms['favorites'][] = array(
'term' => $channel_id,
'params' => array(
'channel_id' => $channel_id
)
);
$connected_accounts_in_feed[ $channel_id ] = $an_account;
} else {
$feed_type_and_terms['favorites'][] = array(
'term' => $channel,
'params' => array(
'channel_name' => $channel
)
);
$connected_accounts_in_feed[ $channel ] = $an_account;
}
} else {
$feed_type_and_terms['favorites'][] = array(
'term' => $channel,
'params' => array(
'channel_id' => $channel
)
);
$connected_accounts_in_feed[ $channel ] = $an_account;
}
}
} elseif ( ! empty( $this->settings['channel'] ) ) {
$channel_array = is_array( $this->settings['channel'] ) ? $this->settings['channel'] : explode( ',', str_replace( ' ', '', $this->settings['channel'] ) );
$an_account = array();
foreach ( $this->connected_accounts as $account ) {
if ( empty( $an_account ) ) {
$an_account = $account;
}
}
foreach ( $channel_array as $channel ) {
if ( strpos( $channel, 'UC' ) !== 0 ) {
$channel_id = sby_get_channel_id_from_channel_name( $channel );
if ( $channel_id ) {
$feed_type_and_terms['favorites'][] = array(
'term' => $channel_id,
'params' => array(
'channel_id' => $channel_id
)
);
$connected_accounts_in_feed[ $channel_id ] = $an_account;
} else {
$feed_type_and_terms['favorites'][] = array(
'term' => $channel,
'params' => array(
'channel_name' => $channel
)
);
$connected_accounts_in_feed[ $channel ] = $an_account;
}
} else {
$feed_type_and_terms['favorites'][] = array(
'term' => $channel,
'params' => array(
'channel_id' => $channel
)
);
$connected_accounts_in_feed[ $channel ] = $an_account;
}
}
}
} elseif ( ! $channel_only && $this->settings['type'] === 'single' ) {
$feed_type_and_terms = array( 'single' => array() );
$videos_array = array();
if ( ! empty( $this->settings['id'] ) ) {
$videos_array = is_array( $this->settings['id'] ) ? $this->settings['id'] : explode( ',', str_replace( ' ', '', $this->settings['id'] ) );
} elseif ( ! empty( $this->settings['single'] ) ) {
$videos_array = is_array( $this->settings['single'] ) ? $this->settings['single'] : explode( ',', str_replace( ' ', '', $this->settings['single'] ) );
}
$filtered_vids = array();
foreach ( $videos_array as $single_video ) {
if ( strpos( $single_video, '=' ) === false ) {
$filtered_vids[] = $single_video;
} else {
$exploded = explode( '&', $single_video );
$filtered_vids[] = $exploded[0];
}
}
$videos_array = $filtered_vids;
if ( ! empty( $videos_array ) ) {
$feed_type_and_terms['single'][] = array(
'term' => implode( '', $videos_array ),
'params' => array(
'video_ids' => $videos_array
)
);
$connected_accounts_in_feed[ implode( '', $videos_array ) ] = $connected_account;
}
} else {
$feed_type_and_terms = array( 'channels' => array() );
if ( ! empty( $this->settings['id'] ) ) {
$channel_array = is_array( $this->settings['id'] ) ? $this->settings['id'] : explode( ',', str_replace( ' ', '', $this->settings['id'] ) );
foreach ( $channel_array as $channel ) {
if ( isset( $this->connected_accounts[ $channel ] ) ) {
$feed_type_and_terms['channels'][] = array(
'term' => $this->connected_accounts[ $channel ]['channel_id'],
'params' => array(
'channel_id' => $this->connected_accounts[ $channel ]['channel_id']
)
);
$connected_accounts_in_feed[ $this->connected_accounts[ $channel ]['channel_id'] ] = $this->connected_accounts[ $channel ];
}
}
if ( empty( $connected_accounts_in_feed ) ) {
$an_account = array();
foreach ( $this->connected_accounts as $account ) {
if ( empty( $an_account ) ) {
$an_account = $account;
}
}
foreach ( $channel_array as $channel ) {
$feed_type_and_terms['channels'][] = array(
'term' => $channel,
'params' => array(
'channel_id' => $channel
)
);
$connected_accounts_in_feed[ $channel ] = $an_account;
}
}
} elseif ( ! empty( $this->settings['channel'] ) ) {
$channel_array = is_array( $this->settings['channel'] ) ? $this->settings['channel'] : explode( ',', str_replace( ' ', '', $this->settings['channel'] ) );
$an_account = array();
foreach ( $this->connected_accounts as $account ) {
if ( empty( $an_account ) ) {
$an_account = $account;
}
}
foreach ( $channel_array as $channel ) {
if ( strpos( $channel, 'UC' ) !== 0 ) {
$channel_id = sby_get_channel_id_from_channel_name( $channel );
if ( $channel_id ) {
$feed_type_and_terms['channels'][] = array(
'term' => $channel_id,
'params' => array(
'channel_id' => $channel_id
)
);
$connected_accounts_in_feed[ $channel_id ] = $an_account;
} else {
$feed_type_and_terms['channels'][] = array(
'term' => $channel,
'params' => array(
'channel_name' => $channel
)
);
$connected_accounts_in_feed[ $channel ] = $an_account;
}
} else {
$feed_type_and_terms['channels'][] = array(
'term' => $channel,
'params' => array(
'channel_id' => $channel
)
);
$connected_accounts_in_feed[ $channel ] = $an_account;
}
}
} else {
foreach ( $this->connected_accounts as $connected_account ) {
if ( empty( $feed_type_and_terms['channels'] ) && is_array($connected_account) ) {
$feed_type_and_terms['channels'][] = array(
'term' => $connected_account['channel_id'],
'params' => array(
'channel_id' => $connected_account['channel_id']
)
);
$connected_accounts_in_feed[ $connected_account['channel_id'] ] = $connected_account;
}
}
}
}
$this->connected_accounts_in_feed = $connected_accounts_in_feed;
$this->feed_type_and_terms = $feed_type_and_terms;
}
/**
* Uses the feed types and terms as well as as some
* settings to create a semi-unique feed id used for
* caching and other features.
*
* Overwritten in the Pro version.
*
* @param string $transient_name
*
* @since 1.0
*/
public function set_transient_name( $transient_name = '' ) {
if ( ! empty( $transient_name ) ) {
$this->transient_name = $transient_name;
} elseif ( false && ! empty( $this->settings['feedid'] ) ) { //disabled due to new caching system not yet being used
$this->transient_name = 'sby_' . $this->settings['feedid'];
} else {
$feed_type_and_terms = $this->feed_type_and_terms;
$sby_transient_name = 'sby_';
$sby_include_words = isset( $this->settings['includewords'] ) ? $this->settings['includewords'] : '';
$sby_exclude_words = isset( $this->settings['excludewords'] ) ? $this->settings['excludewords'] : '';
$cache_string_include = '';
$cache_string_exclude = '';
//Convert include words array into a string consisting of 3 chars each
if ( ! empty( $sby_include_words ) ) {
$sby_include_words_arr = explode(',', $sby_include_words);
foreach( $sby_include_words_arr as $word ){
$include_word = str_replace( str_split(' #'), '', $word );
$cache_string_include .= substr( str_replace('%','', urlencode( $include_word ) ), 0, 3 );
}
}
//Convert exclude words array into a string consisting of 3 chars each
if ( ! empty( $sby_exclude_words ) ) {
$sby_exclude_words_arr = explode( ',', $sby_exclude_words );
foreach( $sby_exclude_words_arr as $word ){
$exclude_word = str_replace( str_split( ' #' ) , '', $word );
$cache_string_exclude .= substr( str_replace( '%','', urlencode( $exclude_word ) ), 0, 3 );
}
}
//Figure out how long the first part of the caching string should be
$cache_string_include_length = strlen( $cache_string_include );
$cache_string_exclude_length = strlen( $cache_string_exclude );
$cache_string_length = $cache_string_include_length + $cache_string_exclude_length;
if ( isset( $feed_type_and_terms['channels'] ) ) {
foreach ( $feed_type_and_terms['channels'] as $term_and_params ) {
$channel = $term_and_params['term'];
$sby_transient_name .= $channel;
}
} elseif ( isset( $feed_type_and_terms['playlist'] ) ) {
foreach ( $feed_type_and_terms['playlist'] as $term_and_params ) {
$playlist = substr( $term_and_params['term'], 0, 6 );
$playlist_end = substr( $term_and_params['term'], -7 );
$sby_transient_name .= $playlist . $playlist_end;
}
} elseif ( isset( $feed_type_and_terms['search'] ) ) {
foreach ( $feed_type_and_terms['search'] as $term_and_params ) {
$sby_transient_name .= 'Q?';
$search = $term_and_params['term'];
$length = strlen( $search );
$sby_transient_name .= substr( $search, 0, 15 );
if ( $length > 15 ) {
$sby_transient_name .= substr( $search, -5, 5 );
}
}
} elseif ( isset( $feed_type_and_terms['live'] ) ) {
foreach ( $feed_type_and_terms['live'] as $term_and_params ) {
$sby_transient_name .= $term_and_params['term'];
}
} elseif ( isset( $feed_type_and_terms['favorites'] ) ) {
foreach ( $feed_type_and_terms['favorites'] as $term_and_params ) {
$sby_transient_name .= 'F!';
$channel = $term_and_params['term'];
$sby_transient_name .= $channel;
}
} elseif ( isset( $feed_type_and_terms['single'] ) ) {
foreach ( $feed_type_and_terms['single'] as $term_and_params ) {
$sby_transient_name .= 'S!';
$video = $term_and_params['term'];
$sby_transient_name .= $video;
}
}
$num = $this->settings['num'];
$num_length = strlen( $num ) + 1;
//Add both parts of the caching string together and make sure it doesn't exceed 45
$sby_transient_name = substr( $sby_transient_name, 0, 45 - $num_length - $cache_string_length ) . $cache_string_include . $cache_string_exclude;
$sby_transient_name .= '#' . $num;
$this->transient_name = $sby_transient_name;
}
}
}

View File

@@ -0,0 +1,134 @@
<?php
namespace SmashBalloon\YouTubeFeed\Pro;
class SBY_YT_Details_Query
{
private $vid_details;
private $vid_ids;
private $cache_expiration_time;
public function __construct( $args, $settings = array() ) {
if ( isset( $args['video_ids'] ) ) {
if ( is_array( $args['video_ids'] ) ) {
$this->vid_ids = $args['video_ids'];
} else {
$this->vid_ids = explode( ',', $args['video_ids'] );
}
}
if ( isset( $settings['details_cache_time'] ) ) {
$this->cache_expiration_time = $settings['details_cache_time'];
} else {
$this->cache_expiration_time = 60 * 2;
}
$this->vid_details = $this->get_cached_details_for_posts();
}
public function get_video_details_to_update() {
$vids_to_retrieve = array();
$first_connected = sby_get_first_connected_account();
if ( ! isset( $first_connected['api_key'] ) ) {
return array();
}
$params = array();
$parts = array( 'id', 'statistics' );
foreach ( $this->vid_details as $video ) {
if ( ! isset( $video['sby_last_details_check_time'] )
|| strtotime( $video['sby_last_details_check_time'] ) < (time() - $this->cache_expiration_time) ) {
if ( $video['sby_description'] === null
|| substr( $video['sby_description'], -3 ) === '...' ) {
if ( ! in_array( 'snippet', $parts, true ) ) {
$parts[] = 'snippet';
}
}
$live_broadcast_content = SBY_Parse_Pro::get_live_broadcast_content( $video );
if ( ! empty( $live_broadcast_content )
&& $live_broadcast_content === 'upcoming' || $live_broadcast_content === 'live' || $live_broadcast_content === 'completed' ) {
if ( ! in_array( 'liveStreamingDetails', $parts, true ) ) {
$parts[] = 'liveStreamingDetails';
}
}
$vids_to_retrieve[] = $video['sby_video_id'];
}
}
$params['part'] = $parts;
if ( ! empty( $vids_to_retrieve ) ) {
$params['id'] = implode( ',', $vids_to_retrieve );
$video_data_connection = new SBY_API_Connect_Pro( $first_connected, 'videos', $params );
$video_data_connection->connect();
$data = $video_data_connection->get_data();
if ( isset( $data['items'] ) ) {
return $data['items'];
}
}
return array();
}
public function get_cached_details_for_posts() {
global $wpdb;
$in_clause = "";
foreach ( $this->vid_ids as $id ) {
$in_clause .= "'" . esc_sql( $id ) . "',";
}
$in_clause = substr( $in_clause, 0, -1 );
$vid_details = $wpdb->get_results( "
SELECT Max(CASE
WHEN m.meta_key = 'sby_video_id' THEN m.meta_value
ELSE NULL
END) AS sby_video_id,
Max(CASE
WHEN m.meta_key = 'sby_last_details_check_time' THEN m.meta_value
ELSE NULL
END) AS sby_last_details_check_time,
Max(CASE
WHEN m.meta_key = 'sby_description' THEN m.meta_value
ELSE NULL
END) AS sby_description,
Max(CASE
WHEN m.meta_key = 'sby_comment_count' THEN m.meta_value
ELSE NULL
END) AS sby_comment_count,
Max(CASE
WHEN m.meta_key = 'sby_like_count' THEN m.meta_value
ELSE NULL
END) AS sby_like_count,
Max(CASE
WHEN m.meta_key = 'sby_view_count' THEN m.meta_value
ELSE NULL
END) AS sby_view_count,
Max(CASE
WHEN m.meta_key = 'sby_live_broadcast_content' THEN m.meta_value
ELSE NULL
END) AS sby_live_broadcast_content,
Max(CASE
WHEN m.meta_key = 'sby_actual_start_time' THEN m.meta_value
ELSE NULL
END) AS sby_actual_start_time,
Max(CASE
WHEN m.meta_key = 'sby_actual_end_time' THEN m.meta_value
ELSE NULL
END) AS sby_actual_end_time,
Max(CASE
WHEN m.meta_key = 'sby_scheduled_start_time' THEN m.meta_value
ELSE NULL
END) AS sby_scheduled_start_time,
m.post_id
FROM $wpdb->postmeta as m
WHERE m.post_id IN (SELECT m2.post_id FROM $wpdb->postmeta as m2 WHERE m2.meta_key = 'sby_video_id' AND m2.meta_value IN ( $in_clause ))
GROUP BY m.post_id", ARRAY_A );
return $vid_details;
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace SmashBalloon\YouTubeFeed\Pro;
class SBY_YT_Query
{
private $posts;
private $sby_youtube_posts_obj;
public function __construct( $args ) {
$args['post_type'] = SBY_CPT;
if ( isset( $args['channel_title'] ) ) {
$args['meta_query'][] = array(
'value' => $args['channel_title'],
'key' => 'sby_channel_title'
);
} elseif ( isset( $args['channel_id'] ) ) {
$args['meta_query'][] = array(
'value' => $args['channel_id'],
'key' => 'sby_channel_id'
);
} elseif ( isset( $args['video_id'] ) ) {
$args['meta_query'][] = array(
'value' => $args['video_id'],
'key' => 'sby_video_id'
);
} elseif ( isset( $args['sbys'] ) ) {
$search_in = isset( $args['sbys_in'] ) ? $args['sbys_in'] : array( 'description' );
foreach ( $search_in as $meta_key ) {
if ( $meta_key !== 'title' ) {
$args['meta_query'][] = array(
'value' => $args['sbys'],
'key' => 'sby_' . $meta_key,
'compare' => 'LIKE'
);
}
}
}
$sby_posts = new \WP_Query( $args );
$this->sby_youtube_posts_obj = $sby_posts;
if ( $sby_posts->have_posts() ) {
$this->posts = $sby_posts->posts;
} else {
$this->posts = array();
}
}
public function get_posts() {
return $this->posts;
}
public static function get_unique_channel_titles() {
global $wpdb;
$unique_channels = $wpdb->get_col( "
SELECT m.meta_value
FROM $wpdb->postmeta as m
WHERE m.meta_key = 'sby_channel_title'
GROUP BY m.meta_value" );
return $unique_channels;
}
public static function get_unique_channel_ids() {
global $wpdb;
$unique_channels = $wpdb->get_col( "
SELECT m.meta_value
FROM $wpdb->postmeta as m
WHERE m.meta_key = 'sby_channel_id'
GROUP BY m.meta_value" );
return $unique_channels;
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* AdminAjaxServicePro
*
* @since 2.3.3
*/
namespace SmashBalloon\YouTubeFeed\Pro\Services;
use Smashballoon\Stubs\Services\ServiceProvider;
use SmashBalloon\YouTubeFeed\Pro\SBY_API_Connect_Pro;
use SmashBalloon\YouTubeFeed\Pro\SBY_Settings_Pro;
class AdminAjaxServicePro extends ServiceProvider {
public function register() {
add_action('wp_ajax_sby_get_comments', [$this, 'sby_get_comments']);
add_action('wp_ajax_nopriv_sby_get_comments', [$this, 'sby_get_comments']);
}
/**
* Retrieves comments for a specific video.
* @return void
* @since 2.3.3
*/
public function sby_get_comments()
{
$video_id = !empty($_POST['video_id']) ? $_POST['video_id'] : '';
if (empty($video_id)) {
wp_send_json_error('Error: Video ID is invalid');
}
$atts = isset($_POST['atts']) ? json_decode(stripslashes($_POST['atts']), true) : null;
if (is_array($atts)) {
array_map('sanitize_text_field', $atts);
} else {
$atts = array();
}
$database_settings = sby_get_database_settings();
$youtube_feed_settings = new SBY_Settings_Pro($atts, $database_settings);
if (empty($database_settings['connected_accounts']) && empty($database_settings['api_key'])) {
wp_send_json_error('Error: No connected account');
}
$video_id = sanitize_text_field($_POST['video_id']);
$settings = $youtube_feed_settings->get_settings();
$comment_count = isset($settings['numcomments']) ? $settings['numcomments'] : 5;
$enable_comments = isset($settings['enablecomments']) ? (bool)$settings['enablecomments'] : false;
if (!empty($video_id) && true === $enable_comments) {
$get_cache = get_transient('sby_comment_cache');
$cache = json_decode($get_cache);
if (!isset($cache->$video_id->etag) ) {
$params = array(
'num' => (int) $comment_count,
'video_id' => $video_id
);
$connection = new SBY_API_Connect_Pro(sby_get_first_connected_account(), 'comments', $params);
$connection->connect();
$comment_data = array(
$video_id => $connection->get_data()
);
$current_set_cache = $comment_data;
if ($cache) {
$cache->$video_id = $comment_data[$video_id];
$current_set_cache = $cache;
}
set_transient('sby_comment_cache', json_encode($current_set_cache));
}
$updated_cache = get_transient('sby_comment_cache');
if (!empty($updated_cache)) {
$cache = json_decode(get_transient('sby_comment_cache'));
echo wp_json_encode($cache->$video_id);
}
die();
}
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace SmashBalloon\YouTubeFeed\Pro\Services;
use SmashBalloon\YouTubeFeed\Services\ServiceContainer;
use SmashBalloon\YouTubeFeed\Pro\Services\AdminAjaxServicePro;
class ServiceContainerPro extends ServiceContainer
{
protected $services = [
AdminAjaxServicePro::class,
];
}