????

Your IP : 216.73.216.110


Current Path : /home/mountain/public_html/wp-content/plugins/socialfeeds/main/
Upload File :
Current File : /home/mountain/public_html/wp-content/plugins/socialfeeds/main/ajax.php

<?php

namespace SocialFeeds;

if(!defined('ABSPATH')){
	exit;
}

class Ajax{

	static function hooks(){
		add_action('wp_ajax_socialfeeds_save_settings', '\SocialFeeds\Ajax::save_settings');
		add_action('wp_ajax_socialfeeds_youtube_preview_show', '\SocialFeeds\Ajax::youtube_preview');
		add_action('wp_ajax_socialfeeds_save_cache_settings', '\SocialFeeds\Ajax::save_cache_settings');
		add_action('wp_ajax_socialfeeds_clear_cache', '\SocialFeeds\Ajax::clear_cache');
		add_action('wp_ajax_socialfeeds_delete_feeds', '\SocialFeeds\Ajax::delete_feed');
		add_action('wp_ajax_socialfeeds_load_more_videos', '\SocialFeeds\Ajax::load_more_videos');
		add_action('wp_ajax_nopriv_socialfeeds_load_more_videos', '\SocialFeeds\Ajax::load_more_videos');
		add_action('wp_ajax_socialfeeds_update_feed_name', '\SocialFeeds\Ajax::update_feed_name');
	}

	static function save_settings(){
		check_ajax_referer('socialfeeds_admin_nonce', 'nonce');
		
		if(!current_user_can('manage_options')){
			wp_send_json_error(__('You do not have required permission to edit this file.', 'socialfeeds'));
		}

		$youtube_opts = get_option('socialfeeds_youtube_option', []);
		
		if(!is_array($youtube_opts)){
			$youtube_opts = [];
		}
		
		// 1. YouTube API Key
		if(isset($_POST['youtube_api_key'])){
			$youtube_opts['youtube_api_key'] = !empty($_POST['youtube_api_key']) ? sanitize_text_field(wp_unslash($_POST['youtube_api_key'])) : '';
		}

		// 2. Wizard Feed Save
		if(isset($_POST['feed_type'])){
			$feed_type = sanitize_text_field(wp_unslash($_POST['feed_type']));

			$feed_settings = [
				'youtube_display_style' => isset($_POST['youtube_display_style']) ? sanitize_text_field(wp_unslash($_POST['youtube_display_style'])) : 'grid',
				'youtube_grid_columns_desktop' => isset($_POST['youtube_grid_columns_desktop']) ? absint(wp_unslash($_POST['youtube_grid_columns_desktop'])): 3,
				'youtube_grid_columns_mobile' => isset($_POST['youtube_grid_columns_mobile']) ? absint(wp_unslash($_POST['youtube_grid_columns_mobile'])) : 1,
				'youtube_color_scheme' => isset($_POST['youtube_color_scheme']) ? sanitize_text_field(wp_unslash($_POST['youtube_color_scheme'])) : 'light',
				'youtube_custom_color' => isset($_POST['youtube_custom_color']) ? sanitize_hex_color(wp_unslash($_POST['youtube_custom_color'])) : '#000000',
				'youtube_header_enabled' => isset($_POST['youtube_header_enabled']) ? 1 : 0,
				'youtube_header_show_channel_name' => isset($_POST['youtube_header_show_channel_name']) ? 1 : 0,
				'youtube_header_show_logo' => isset($_POST['youtube_header_show_logo']) ? 1 : 0,
				'youtube_header_show_subscribers' => isset($_POST['youtube_header_show_subscribers']) ? 1 : 0,
				'youtube_header_show_banner' => isset($_POST['youtube_header_show_banner']) ? 1 : 0, 
				'youtube_header_show_description' => isset($_POST['youtube_header_show_description']) ? 1 : 0,
				'youtube_header_text' => isset($_POST['youtube_header_text']) ? sanitize_text_field(wp_unslash($_POST['youtube_header_text'])) : '',
				'youtube_load_more_enabled' => isset($_POST['youtube_load_more_enabled']) ? 1 : 0,
				'youtube_load_more_text' => isset($_POST['youtube_load_more_text']) ? sanitize_text_field(wp_unslash($_POST['youtube_load_more_text'])) : 'Load More',
				'youtube_load_more_bg_color' => isset($_POST['youtube_load_more_bg_color']) ? sanitize_hex_color(wp_unslash($_POST['youtube_load_more_bg_color'])) : '#FF0000',
				'youtube_load_more_text_color' => isset($_POST['youtube_load_more_text_color']) ? sanitize_hex_color(wp_unslash($_POST['youtube_load_more_text_color'])) : '#FFFFFF',
				'youtube_load_more_hover_color' => isset($_POST['youtube_load_more_hover_color']) ? sanitize_hex_color(wp_unslash($_POST['youtube_load_more_hover_color'])) : '#5f0505',
				'youtube_load_more_count' => isset($_POST['youtube_load_more_count']) ? intval($_POST['youtube_load_more_count']) : 9,
				'youtube_show_title' => isset($_POST['youtube_show_title']) ? 1 : 0,
				'youtube_show_desc' => isset($_POST['youtube_show_desc']) ? 1 : 0,
				'youtube_show_play_icon' => isset($_POST['youtube_show_play_icon']) ? 1 : 0,
				'youtube_show_duration' => isset($_POST['youtube_show_duration']) ? 1 : 0,
				'youtube_show_date' => isset($_POST['youtube_show_date']) ? 1 : 0,
				'youtube_show_views' => isset($_POST['youtube_show_views']) ? 1 : 0,
				'youtube_show_likes' => isset($_POST['youtube_show_likes']) ? 1 : 0,
				'youtube_show_comments' => isset($_POST['youtube_show_comments']) ? 1 : 0,
				'youtube_lazy_load' => isset($_POST['youtube_lazy_load']) ? 1 : 0,
				'youtube_videos_per_page' => isset($_POST['youtube_videos_per_page']) ? intval($_POST['youtube_videos_per_page']) : 12,
				'youtube_spacing' => isset($_POST['youtube_spacing']) ? intval($_POST['youtube_spacing']) : 16,
				'youtube_hover_effect' => isset($_POST['youtube_hover_effect']) ? sanitize_text_field(wp_unslash($_POST['youtube_hover_effect'])) : 'overlay',
				'youtube_click_action' => isset($_POST['youtube_click_action']) ? sanitize_text_field(wp_unslash($_POST['youtube_click_action'])) : 'newtab',
				'youtube_subscribe_button_enabled' => isset($_POST['youtube_subscribe_button_enabled']) ? 1 : 0,
				'youtube_subscribe_text' => isset($_POST['youtube_subscribe_text']) ? sanitize_text_field(wp_unslash($_POST['youtube_subscribe_text'])) : 'Subscribe',
				'youtube_subscribe_bg_color' => isset($_POST['youtube_subscribe_bg_color']) ? sanitize_hex_color(wp_unslash($_POST['youtube_subscribe_bg_color'])) : '#FF0000',
				'youtube_subscribe_text_color' => isset($_POST['youtube_subscribe_text_color']) ? sanitize_hex_color(wp_unslash($_POST['youtube_subscribe_text_color'])) : '#FFFFFF',
				'youtube_subscribe_hover_color' => isset($_POST['youtube_subscribe_hover_color']) ? sanitize_hex_color(wp_unslash($_POST['youtube_subscribe_hover_color'])) : '#CC0000',
			];

			$feeds = isset($youtube_opts['youtube_feeds']) ? $youtube_opts['youtube_feeds'] : [];
			$input_val = '';

			if(isset($_POST['channel_id'])){
				$input_val = sanitize_text_field(wp_unslash($_POST['channel_id']));
			} elseif(isset($_POST['playlist_id'])){
				$input_val = sanitize_text_field(wp_unslash($_POST['playlist_id']));
			} elseif(isset($_POST['search_term'])){
				$input_val = sanitize_text_field(wp_unslash($_POST['search_term']));
			} elseif(isset($_POST['socialwall_feeds']) && is_array($_POST['socialwall_feeds'])){
				$sel = array_map('sanitize_text_field', wp_unslash($_POST['socialwall_feeds']));
				$input_val = implode(',', $sel);
			} elseif(isset($_POST['video_ids'])){
				$input_val = sanitize_text_field(wp_unslash($_POST['video_ids']));
			}

			if(!empty($input_val)){
				$preview = isset($_POST['preview_url']) ? rawurldecode(sanitize_text_field(wp_unslash($_POST['preview_url']))) : '';
				$edit_id = isset($_POST['edit_id']) ? sanitize_text_field(wp_unslash($_POST['edit_id'])) : '';
				$channel_subscriber_count = isset($_POST['channel_subscriber_count']) ? sanitize_text_field(wp_unslash($_POST['channel_subscriber_count'])) : 0;
				
				if ($edit_id) {
					$updated = false;
					foreach($feeds as $k => $f){
						if(isset($f['id']) && (string) $f['id'] === (string) $edit_id){
							$feeds[$k]['settings'] = $feed_settings;
							$feeds[$k]['input'] = $input_val;
							$feeds[$k]['preview'] = $preview;
							$feeds[$k]['subscriber_count'] = $channel_subscriber_count;
							$updated = true;
							break;
						}
					}
					if(!$updated) $edit_id = '';
				}

				if(!$edit_id){
					$same_type_count = 0;
					foreach($feeds as $f){
						if(isset($f['type']) && $f['type'] === $feed_type) $same_type_count++;
					}
					$index = $same_type_count + 1;
					$name = 'YouTube Feed - ' . ucfirst($feed_type) . ' ' . $index;
					$feed_id = isset($_POST['client_feed_id']) ? sanitize_text_field(wp_unslash($_POST['client_feed_id'])) : '';
				
					if(empty($feed_id)){
						// Get global ID counter
						$global_counter = get_option('socialfeeds_global_id_counter', 0);
					
						// Collect all existing IDs from YouTube, Instagram and Facebook
						$all_existing_ids = [];
					
						// YouTube IDs
						foreach ($feeds as $f) {
							if (isset($f['id'])) {
								$all_existing_ids[] = intval($f['id']);
							}
						}
					
						// Instagram IDs
						$instagram_opts = get_option('socialfeeds_instagram_option', []);
						$instagram_feeds = isset($instagram_opts['instagram_feeds']) ? $instagram_opts['instagram_feeds'] : [];
						foreach($instagram_feeds as $f){
							if(isset($f['id'])){
								$all_existing_ids[] = intval($f['id']);
							}
						}

						// Facebook IDs
						$facebook_opts = get_option('socialfeeds_facebook_option', []);
						$facebook_feeds = isset($facebook_opts['facebook_feeds']) ? $facebook_opts['facebook_feeds'] : [];
						foreach($facebook_feeds as $f){
							if(isset($f['id'])){
								$all_existing_ids[] = intval($f['id']);
							}
						}
					
						// Find next available ID
						// If no feeds exist, start from 1 and reset counter
						if(empty($all_existing_ids)){
							$next_id = 1;
							// Reset global counter
							update_option('socialfeeds_global_id_counter', 2);
						} else {
							$next_id = max($global_counter, 1);
							while(in_array($next_id, $all_existing_ids)){
								$next_id++;
							}
							// Update global counter
							update_option('socialfeeds_global_id_counter', $next_id + 1);
						}
					
						$feed_id = $next_id;
					}
				
					$feeds[] = [
						'id' => $feed_id,
						'name' => $name,
						'shortcode' => '[socialfeeds id="' . $feed_id . '" platform="youtube"]',
						'type' => $feed_type,
						'input' => $input_val,
						'preview' => $preview,
						'settings' => $feed_settings,
						'subscriber_count' => $channel_subscriber_count,
						'created' => time(),
					];
				}

				$youtube_opts['youtube_feeds'] = $feeds;
				update_option('social_feed_youtube_settings', $feed_settings);
			}
		}

		// 3. Handle structured options from complex forms
		if(isset($_POST['social_feed_options']['youtube'])){
			$youtube_opts['youtube_channel_id'] = !empty($_POST['social_feed_options']['youtube']['channel_id']) ? sanitize_text_field(wp_unslash($_POST['social_feed_options']['youtube']['channel_id'])) : '';
			$youtube_opts['youtube_display_style'] = !empty($_POST['social_feed_options']['youtube']['display_style']) ? sanitize_text_field(wp_unslash($_POST['social_feed_options']['youtube']['display_style'])) : '';
			$youtube_opts['youtube_theme'] = !empty($_POST['social_feed_options']['youtube']['theme']) ? sanitize_text_field(wp_unslash($_POST['social_feed_options']['youtube']['theme'])) : '';

			$youtube_opts['youtube_click_action'] = !empty($_POST['social_feed_options']['youtube']['click_action']) ? sanitize_text_field(wp_unslash($_POST['social_feed_options']['youtube']['click_action'])) : '';
			$youtube_opts['youtube_url'] = !empty($_POST['social_feed_options']['youtube']['url']) ? esc_url_raw(wp_unslash($_POST['social_feed_options']['youtube']['url'])) : '';
			$youtube_opts['youtube_header_text'] = !empty($_POST['social_feed_options']['youtube']['header_text']) ? sanitize_text_field(wp_unslash($_POST['social_feed_options']['youtube']['header_text'])) : '';
			$youtube_opts['youtube_grid_columns'] = !empty($_POST['social_feed_options']['youtube']['grid_columns']) ? intval(wp_unslash($_POST['social_feed_options']['youtube']['grid_columns'])) : 3;
			
			$youtube_bools = [
				'youtube_show_title', 'youtube_show_desc', 'youtube_show_play_icon', 'youtube_show_duration',
				'youtube_show_date', 'youtube_show_views', 'youtube_show_likes', 'youtube_show_comments',
				'youtube_carousel_autoplay', 'youtube_header_enabled', 'youtube_header_show_channel_name',
				'youtube_header_show_logo', 'youtube_header_show_subscribers', 'youtube_lightbox_enabled', 'youtube_load_more_enabled',
				'youtube_subscribe_button_enabled', 'youtube_video_autoplay', 'instagram_display_videos', 'instagram_show_reels',
			];
			
			foreach($youtube_bools as $bool){
				$youtube_opts['social_feed_youtube_' . $bool] = (isset($_POST['social_feed_options']['youtube'][$bool]) && !empty($_POST['social_feed_options']['youtube'][$bool])) ? 1 : 0;
			}
		}

		update_option('socialfeeds_youtube_option', $youtube_opts);

		$response = [
			'message' => esc_html__('Settings saved successfully.', 'socialfeeds'),
			'feed_id' => !empty($feed_id) ? $feed_id : '',
			'feed_name' => !empty($name) ? $name : '',
		];

		wp_send_json_success($response);
	}
	
	static function youtube_preview(){
		check_ajax_referer('socialfeeds_admin_nonce', 'nonce');
		
		if(!current_user_can('manage_options')){
			wp_send_json_error(__('You do not have required permission to edit this file.', 'socialfeeds'));
		}
		
		$feed_type = isset($_POST['feed_type']) ? sanitize_text_field(wp_unslash($_POST['feed_type'])) : '';
		$page_token = isset($_POST['pageToken']) ? sanitize_text_field(wp_unslash($_POST['pageToken'])) : '';

		$option = get_option('socialfeeds_youtube_option', []);
		$api_key = !empty($option['youtube_api_key']) ? $option['youtube_api_key'] : '';

		$per_page = isset($_POST['youtube_videos_per_page']) ? absint(wp_unslash($_POST['youtube_videos_per_page'])) : 6;
		$per_page = max(1, min(50, $per_page));

		if(empty($api_key)){
			wp_send_json_error(__('YouTube API key not configured.', 'socialfeeds'));
		}

		$items = [];
		$next_page_token = '';
		$channel_id = '';

		/* ---------------- CHANNEL (FREE) ---------------- */
		if($feed_type === 'channel'){
			$input = isset($_POST['channel_id']) ? sanitize_text_field(wp_unslash($_POST['channel_id'])) : '';

			if(!empty($input)){
				if(strpos($input, 'UC') === 0){
					$channel_id = $input;
				} else{
					$body = self::fetch_url(add_query_arg([
						'key' => $api_key,
						'part' => 'snippet',
						'type' => 'channel',
						'maxResults' => 1,
						'q' => $input,
					], 'https://www.googleapis.com/youtube/v3/search'));

					$channel_id = isset($body['items'][0]['id']['channelId']) ? $body['items'][0]['id']['channelId'] : '';
					if(!$channel_id){
						wp_send_json_error(__('Channel not found. Please check the channel name or ID.', 'socialfeeds'));
					}
				}
			}

			if(!empty($channel_id)){
				$query_args = [
					'key' => $api_key,
					'part' => 'snippet',
					'channelId' => $channel_id,
					'order' => 'date',
					'type' => 'video',
					'maxResults' => $per_page,
				];
				
				if(!empty($page_token)){
					$query_args['pageToken'] = $page_token;
				}
				
				$url = add_query_arg($query_args, 'https://www.googleapis.com/youtube/v3/search');

				$body = self::fetch_url($url);
				
				$next_page_token = isset($body['nextPageToken']) ? $body['nextPageToken'] : '';

				foreach(isset($body['items']) ? $body['items'] : array() as $it){
					if(empty($it['id']['videoId'])){
						continue;
					}
					
					$snippet = isset($it['snippet']) ? $it['snippet'] : [];
					$items[] = array(
						'videoId' => isset($it['id']['videoId']) ? $it['id']['videoId'] : '',
						'title' => isset($snippet['title']) ? $snippet['title'] : '',
						'description' => isset($snippet['description']) ? $snippet['description'] : '',
						'thumbnails' => isset($snippet['thumbnails']) ? $snippet['thumbnails'] : [],
						'channelTitle' => isset($snippet['channelTitle']) ? $snippet['channelTitle'] : (isset($snippet['videoOwnerChannelTitle']) ? $snippet['videoOwnerChannelTitle'] : ''),
						'channelId'	=> isset($snippet['channelId']) ? $snippet['channelId'] : (isset($snippet['videoOwnerChannelId']) ? $snippet['videoOwnerChannelId'] : ''),
					);
				}
			}
		} else {

			if(class_exists('\SocialFeedsPro\YouTube') && method_exists('\SocialFeedsPro\YouTube', 'youtube_preview')){
				\SocialFeedsPro\YouTube::youtube_preview($feed_type, $api_key, $per_page, $page_token);
			}

			/* translators: %s: Feed type name */
			wp_send_json_error(sprintf(__('The %s feed type requires the Pro version.', 'socialfeeds'), $feed_type));
		}

		if(!$items){
			wp_send_json_error(__('No videos found for the provided input.', 'socialfeeds'));
		}

		/* ---------------- VIDEO DETAILS ---------------- */
		$video_ids = array_slice(array_column($items, 'videoId'), 0, $per_page);
		if(!empty($video_ids) && class_exists('\SocialFeedsPro\YouTube') && method_exists('\SocialFeedsPro\YouTube', 'fetch_video_details')){
			$items = \SocialFeedsPro\YouTube::fetch_video_details($items, $api_key, $per_page);
		}

		/* ---------------- CHANNEL INFO ---------------- */
		$preview_channel_id = isset($items[0]['channelId']) ? $items[0]['channelId'] : $channel_id;
		$channel_info = null;

		if(!empty($preview_channel_id)){
			$body = self::fetch_url(add_query_arg([
				'key'  => $api_key,
				'part' => 'snippet,brandingSettings,statistics',
				'id'   => $preview_channel_id,
			], 'https://www.googleapis.com/youtube/v3/channels'));

			$snippet = isset($body['items'][0]['snippet']) ? $body['items'][0]['snippet'] : null;

			if(!empty($snippet)){
				$subscriber_count = isset($body['items'][0]['statistics']['subscriberCount']) ?  $body['items'][0]['statistics']['subscriberCount'] : 0;
				$channel_info = array(
					'id' => $preview_channel_id,
					'title'	=> isset($snippet['title']) ? $snippet['title'] : '',
					'thumbnail' => isset($snippet['thumbnails']['default']['url']) ? $snippet['thumbnails']['default']['url'] : '',
					'description' => isset($snippet['description']) ? $snippet['description'] : '',
					'bannerExternalUrl' => isset($body['items'][0]['brandingSettings']['image']['bannerExternalUrl']) ? $body['items'][0]['brandingSettings']['image']['bannerExternalUrl'] : '',
					'subscriberCount' => $subscriber_count,
				);
			}
		}

		wp_send_json_success([
			'items' => $items,
			'nextPageToken' => $next_page_token,
			'channel' => $channel_info,
		]);
	}

	static function delete_feed(){
		check_ajax_referer('socialfeeds_admin_nonce', 'nonce');

		if(!current_user_can('manage_options')){
			wp_send_json_error(__('You do not have required permission to edit this file.', 'socialfeeds'));
		}

		$feed_id  = isset($_POST['feed_id']) ? sanitize_text_field(wp_unslash($_POST['feed_id'])) : '';
		$platform = isset($_POST['platform']) ? sanitize_text_field(wp_unslash($_POST['platform'])) : 'youtube';

		// Determine which feed list to modify
		if($platform === 'instagram'){
			$opts = get_option('socialfeeds_instagram_option', []);
			$key = 'instagram_feeds';
		} elseif($platform === 'facebook'){
			$opts = get_option('socialfeeds_facebook_option', []);
			$key = 'facebook_feeds';
		} else {
			$opts = get_option('socialfeeds_youtube_option', []);
			$key = 'youtube_feeds';
		}

		$feeds = isset($opts[$key]) ? $opts[$key] : [];

		// Filter out the feed to delete
		if($feed_id && !empty($feeds)){
			$filtered_feeds = [];
			foreach($feeds as $feed){
				if(empty($feed['id']) || (string)$feed['id'] !== (string)$feed_id){
					$filtered_feeds[] = $feed;
				}
			}
			
			$opts[$key] = $filtered_feeds;
			
			if($platform === 'instagram'){
				update_option('socialfeeds_instagram_option', $opts);
			} elseif($platform === 'facebook'){
				update_option('socialfeeds_facebook_option', $opts);
			} else {
				update_option('socialfeeds_youtube_option', $opts);
			}

			wp_send_json_success(['message'  => esc_html__('Feed deleted successfully', 'socialfeeds')]);
		}

		wp_send_json_error(esc_html__('Feed not found or invalid request','socialfeeds'));
	}

	static function load_more_videos(){
		
		check_ajax_referer('socialfeeds_frontend_nonce', 'nonce');

		$feed_id = isset($_POST['feed_id']) ? sanitize_text_field(wp_unslash($_POST['feed_id'])) : '';
		$feed_type = isset($_POST['feed_type']) ? sanitize_text_field(wp_unslash($_POST['feed_type'])) : '';
		$feed_input = isset($_POST['feed_input']) ? sanitize_text_field(wp_unslash($_POST['feed_input'])) : '';
		$limit = isset($_POST['limit']) ? absint(wp_unslash($_POST['limit'])) : 12;
		$pageToken = isset($_POST['pageToken']) ? sanitize_text_field(wp_unslash($_POST['pageToken'])) : '';
		$opts_global = get_option('socialfeeds_youtube_option', []);
		$feed_settings = [];
		$settings_opts = get_option('socialfeeds_settings_option', []);
		$cache_duration = !empty($settings_opts['cache']['duration']) ? absint($settings_opts['cache']['duration']) : '3600';

		// If feed_id is provided, fetch settings from database
		if ($feed_id) {
			$feeds = isset($opts_global['youtube_feeds']) ? $opts_global['youtube_feeds'] : [];
			$found_feed = null;
			foreach ($feeds as $f) {
				if (isset($f['id']) && (string) $f['id'] === (string) $feed_id) {
					$found_feed = $f;
					break;
				}
			}

			if ($found_feed) {
				$feed_type = isset($found_feed['type']) ? $found_feed['type'] : $feed_type;
				$feed_input = isset($found_feed['input']) ? $found_feed['input'] : $feed_input;
				$feed_settings = isset($found_feed['settings']) ? $found_feed['settings'] : [];
				$limit = isset($feed_settings['youtube_load_more_count']) ? intval($feed_settings['youtube_load_more_count']) : $limit;
			}
		}

		$opts = array_merge($opts_global, $feed_settings);

		$api_key = isset($opts_global['youtube_api_key']) ? $opts_global['youtube_api_key'] : '';

		if(empty($feed_type) || empty($api_key)){
			wp_send_json_error(__('Missing required parameters', 'socialfeeds'));
		}

		$items = [];
		$nextPageToken = '';

		/* ---------------- CHANNEL (FREE) ---------------- */
		if($feed_type === 'channel'){
			$channelId = $feed_input;
			
			$search_url = add_query_arg([
				'key' => $api_key,
				'part' => 'snippet',
				'type' => 'channel',
				'maxResults' => 1,
				'q' => $channelId,
			], 'https://www.googleapis.com/youtube/v3/search');

			$body = self::fetch_url($search_url);
			if(!empty($body['items'][0]['id']['channelId'])){
				$channelId = $body['items'][0]['id']['channelId'];
			}

			$url = add_query_arg([
				'key' => $api_key,
				'part' => 'snippet',
				'channelId' => $channelId,
				'order' => 'date',
				'maxResults' => $limit,
				'pageToken' => $pageToken,
			], 'https://www.googleapis.com/youtube/v3/search');

			$body = self::fetch_url($url);
			
			$nextPageToken = isset($body['nextPageToken']) ? $body['nextPageToken'] : '';
			$items = self::extract_videos($body['items']);
			
			// Enrich with statistics/details if filter available (e.g. Pro or custom)
			if(!empty($items)){
				$items = apply_filters('socialfeeds_youtube_video_items_details', $items, $api_key, $limit);
			}

			$response_data = ['items' => $items];
			if(!empty($nextPageToken)){
				$response_data['nextPageToken'] = $nextPageToken;
			}

		} else {
			// PRO
			do_action('socialfeeds_youtube_load_more_pro', $feed_type, $feed_input, $api_key, $limit, $pageToken, '\SocialFeeds\Ajax::extract_videos');
			/* translators: %s: requires the Pro*/
			wp_send_json_error(sprintf(__('Loading more for %s requires the Pro version.', 'socialfeeds'), $feed_type));
		}

		// Generate HTML
		if(!empty($response_data['items']) && class_exists('\SocialFeeds\Shortcodes')){
			$response_data['html'] = \SocialFeeds\Shortcodes::render_items($response_data['items'], $opts);
		}

		wp_send_json_success($response_data);
	}
	
	static function fetch_url($url){
		$resp = wp_remote_get($url);
		
		if(is_wp_error($resp)){
			wp_send_json_error($resp->get_error_message());
		}
		
		$code = wp_remote_retrieve_response_code($resp);
		if(200 !== $code){
			$body = json_decode(wp_remote_retrieve_body($resp), true);
			$error_msg = 'API request failed with code ' . $code;

			if(isset($body['error']['errors'][0]['reason'])){
				$reason = $body['error']['errors'][0]['reason'];
				if($reason === 'quotaExceeded'){
					$error_msg = __('YouTube API Quota Exceeded. Please try again later or use a different API key.', 'socialfeeds');
				} elseif($reason === 'keyInvalid'){
					$error_msg = __('Invalid YouTube API Key. Please check your settings.', 'socialfeeds');
				}
			}
			
			if (isset($body['error']['message'])) {
				$api_message = $body['error']['message'];
				// If we haven't set a specific custom message yet, use the API message
				if ($error_msg === 'API request failed with code ' . $code) {
					$error_msg = sanitize_text_field(wp_unslash($api_message));
				}
			}

			// If it's a 404 and we are checking a channel/resource
			if($code === 404){
				$error_msg = __('Resource not found. Please check your ID or username.', 'socialfeeds');
			}

			wp_send_json_error($error_msg);
		}
		
		return json_decode(wp_remote_retrieve_body($resp), true);
	}

	static function extract_videos($raw_items, $type = 'search'){
		$videos = [];
		foreach($raw_items as $it){
			$vid = '';
			$snippet = isset($it['snippet']) ? $it['snippet'] : [];

			switch($type){
				case 'playlist':
					$vid = isset($it['snippet']['resourceId']['videoId']) ? $it['snippet']['resourceId']['videoId']: '';		
					break;
				default: // channel, search, live
					$vid = isset($it['id']['videoId']) ? $it['id']['videoId'] : '';			
			}

			if(empty($vid)){
				continue;
			}

			$videos[] = [
				'videoId' => $vid,
				'title' => isset($snippet['title']) ? $snippet['title'] : '',
				'description' => isset($snippet['description']) ? $snippet['description'] : '',
				'thumbnails' => isset($snippet['thumbnails']) ? $snippet['thumbnails'] : [],
			];
		}
		return $videos;
	}

	static function save_cache_settings(){
		check_ajax_referer('socialfeeds_admin_nonce', 'nonce');

		if(!current_user_can('manage_options')){
			wp_send_json_error(__('You do not have required permission to edit this file.', 'socialfeeds'));
		}

		$duration['cache_duration'] = isset($_POST['cache_interval']) ? sanitize_text_field(wp_unslash($_POST['cache_interval'])) : '';

		update_option('socialfeeds_settings_option', $duration);

		wp_send_json_success(__('Cache settings saved successfully!', 'socialfeeds'));
	}

	static function clear_cache(){
		check_ajax_referer('socialfeeds_admin_nonce', 'nonce');

		if(!current_user_can('manage_options')){
			wp_send_json_error(__('You do not have required permission to edit this file.', 'socialfeeds'));
		}

		$youtube_opts = get_option('socialfeeds_youtube_option', []);
		$api_key = isset($youtube_opts['youtube_api_key']) ? $youtube_opts['youtube_api_key'] : '';
		$feeds = isset($youtube_opts['youtube_feeds']) ? $youtube_opts['youtube_feeds'] : [];

		foreach($feeds as $feed){
			$type = isset($feed['type']) ? $feed['type'] : 'channel';
			$input = isset($feed['input']) ? trim($feed['input']) : '';
			$settings = isset($feed['settings']) ? $feed['settings'] : [];
			
			$merged_settings = array_merge($youtube_opts, $settings);
			$limit = isset($merged_settings['youtube_videos_per_page']) ? intval($merged_settings['youtube_videos_per_page']) : 12;

			if(empty($input)) continue;

			// Channel feed type (Free feature)
			if($type === 'channel'){
				$channelId = $input;

				if(!preg_match('/^UC/', $channelId)){
					$cache_key_ch_res = 'socialfeeds_yt_channel' . md5($channelId);
					$ch_body = get_transient($cache_key_ch_res);
					if($ch_body && !empty($ch_body['items'][0]['id']['channelId'])){
						$channelId = $ch_body['items'][0]['id']['channelId'];
					}

					delete_transient($cache_key_ch_res);
				}

				// Clear channel videos cache - construct URL to match cache key
				$url = add_query_arg([
					'key' => $api_key,
					'part' => 'snippet',
					'channelId' => $channelId,
					'order' => 'date',
					'type' => 'video',
					'maxResults' => $limit,
				], 'https://www.googleapis.com/youtube/v3/search');
				delete_transient('socialfeeds_yt_channel_name' . md5($url));

				// Clear channel header cache
				delete_transient('socialfeeds_yt_headers' . md5($channelId));

				// Clear channel description cache
				delete_transient('socialfeeds_yt_desc' . md5($channelId));
			}
		}

		// Clear YouTube Pro caches
		do_action('socialfeeds_clear_youtube_cache');
		
		// Clear Instagram caches
		do_action('socialfeeds_clear_instagram_cache');

		wp_send_json_success(__('All caches cleared successfully!', 'socialfeeds'));
	}

	static function update_feed_name(){
		check_ajax_referer('socialfeeds_admin_nonce', 'nonce');

		if(!current_user_can('manage_options')){
			wp_send_json_error(__('You do not have required permission to edit this file.', 'socialfeeds'));
		}

		$feed_id = isset($_POST['feed_id']) ? sanitize_text_field(wp_unslash($_POST['feed_id'])) : '';
		$platform = isset($_POST['platform']) ? sanitize_text_field(wp_unslash($_POST['platform'])) : '';
		$name = isset($_POST['name']) ? sanitize_text_field(wp_unslash($_POST['name'])) : '';

		if(empty($feed_id) || empty($platform) || empty($name)){
			wp_send_json_error(['message' => esc_html__('Missing required fields', 'socialfeeds')]);
		}

		if($platform === 'facebook'){
			$option_key = 'socialfeeds_facebook_option';
			$feeds_key = 'facebook_feeds';
		} else {
			$option_key = $platform === 'youtube' ? 'socialfeeds_youtube_option' : 'socialfeeds_instagram_option';
			$feeds_key = $platform === 'youtube' ? 'youtube_feeds' : 'instagram_feeds';
		}
		$options = get_option($option_key, []);
		$feeds = isset($options[$feeds_key]) ? $options[$feeds_key] : [];

		$updated = false;
		foreach($feeds as $k => $f){
			if(isset($f['id']) && (string)$f['id'] === (string)$feed_id){
				$feeds[$k]['name'] = $name;
				$updated = true;
				break;
			}
		}

		if(empty($updated)){
			wp_send_json_error(['message' => esc_html__('Feed not found', 'socialfeeds')]);
		}

		$options[$feeds_key] = $feeds;
		update_option($option_key, $options);

		wp_send_json_success(['message' => esc_html__('Feed name updated', 'socialfeeds')]);
	}

}