| @@ -30,20 +30,15 @@ | ||
| 30 | 30 | */ |
| 31 | 31 | protected $api_key; |
| 32 | 32 | |
| 33 | 33 | /** |
| 34 | - * The YouTube API URLs. | |
| 34 | + * Array of query params. | |
| 35 | 35 | * |
| 36 | - * @since 1.0.0 | |
| 36 | + * @since 2.5.7 | |
| 37 | 37 | * @access protected |
| 38 | 38 | * @var array |
| 39 | 39 | */ |
| 40 | - public $api_urls = array( | |
| 41 | - 'playlistItems.list' => 'https://www.googleapis.com/youtube/v3/playlistItems', | |
| 42 | - 'channels.list' => 'https://www.googleapis.com/youtube/v3/channels', | |
| 43 | - 'search.list' => 'https://www.googleapis.com/youtube/v3/search', | |
| 44 | - 'videos.list' => 'https://www.googleapis.com/youtube/v3/videos' | |
| 45 | - ); | |
| 40 | + protected $params = array(); | |
| 46 | 41 | |
| 47 | 42 | /** |
| 48 | 43 | * Is development mode enabled? |
| 49 | 44 | * |
| @@ -53,12 +48,26 @@ | ||
| 53 | 48 | */ |
| 54 | 49 | protected $is_development_mode = false; |
| 55 | 50 | |
| 56 | 51 | /** |
| 52 | + * The YouTube API URLs. | |
| 53 | + * | |
| 54 | + * @since 1.0.0 | |
| 55 | + * @access protected | |
| 56 | + * @var array | |
| 57 | + */ | |
| 58 | + public $api_urls = array( | |
| 59 | + 'playlistItems.list' => 'https://www.googleapis.com/youtube/v3/playlistItems', | |
| 60 | + 'channels.list' => 'https://www.googleapis.com/youtube/v3/channels', | |
| 61 | + 'search.list' => 'https://www.googleapis.com/youtube/v3/search', | |
| 62 | + 'videos.list' => 'https://www.googleapis.com/youtube/v3/videos' | |
| 63 | + ); | |
| 64 | + | |
| 65 | + /** | |
| 57 | 66 | * Get videos. |
| 58 | 67 | * |
| 59 | 68 | * @since 1.0.0 |
| 60 | - * @param array $params Array of query params. | |
| 69 | + * @param array $params Array of query params. | |
| 61 | 70 | * @return mixed |
| 62 | 71 | */ |
| 63 | 72 | public function query( $params = array() ) { |
| 64 | 73 | // Get YouTube API Key |
| @@ -64,12 +73,13 @@ | ||
| 64 | 73 | // Get YouTube API Key |
| 65 | 74 | $general_settings = get_option( 'ayg_general_settings' ); |
| 66 | 75 | |
| 67 | 76 | if ( empty( $general_settings['api_key'] ) ) { |
| 68 | - return $this->get_error( __( 'YouTube API key not found.', 'automatic-youtube-gallery' ) . ' ' . sprintf( __( 'Kindly follow this URL <a href="%s" target="_blank">this guide</a> to get your own API key.', 'automatic-youtube-gallery' ), 'https://plugins360.com/automatic-youtube-gallery/how-to-get-youtube-api-key/' ) ); | |
| 77 | + return $this->get_error( __( 'YouTube API key not found.', 'automatic-youtube-gallery' ) . ' ' . sprintf( __( 'Kindly follow this URL <a href="%s" target="_blank" rel="noopener noreferrer">this guide</a> to get your own API key.', 'automatic-youtube-gallery' ), 'https://plugins360.com/automatic-youtube-gallery/how-to-get-youtube-api-key/' ) ); | |
| 69 | 78 | } |
| 70 | 79 | |
| 71 | 80 | $this->api_key = $general_settings['api_key']; |
| 81 | + $this->params = $params; | |
| 72 | 82 | |
| 73 | 83 | // Is development mode enabled? |
| 74 | 84 | if ( isset( $general_settings['development_mode'] ) && ! empty( $general_settings['development_mode'] ) ) { |
| 75 | 85 | $this->is_development_mode = true; |
| @@ -77,8 +87,13 @@ | ||
| 77 | 87 | |
| 78 | 88 | // Process output |
| 79 | 89 | $response = array(); |
| 80 | 90 | |
| 91 | + if ( ! empty( $params['searchTerm'] ) ) { | |
| 92 | + $response = $this->get_videos_from_db( $params ); | |
| 93 | + return $response; | |
| 94 | + } | |
| 95 | + | |
| 81 | 96 | switch ( $params['type'] ) { |
| 82 | 97 | case 'playlist': |
| 83 | 98 | if ( empty( $params['src'] ) ) { |
| 84 | 99 | return $this->get_error( __( 'YouTube Playlist ID (or) URL is required.', 'automatic-youtube-gallery' ) ); |
| @@ -193,9 +208,9 @@ | ||
| 193 | 208 | $id = $url; |
| 194 | 209 | |
| 195 | 210 | switch ( $type ) { |
| 196 | 211 | case 'playlist': |
| 197 | - if ( preg_match( '/list=(.*)&?\/?/', $url, $matches ) ) { | |
| 212 | + if ( preg_match( '/[?&]list=([^&]+)/', $url, $matches ) ) { | |
| 198 | 213 | $id = $matches[1]; |
| 199 | 214 | } |
| 200 | 215 | break; |
| 201 | 216 | |
| @@ -219,14 +234,18 @@ | ||
| 219 | 234 | } |
| 220 | 235 | break; |
| 221 | 236 | |
| 222 | 237 | default: // video |
| 238 | + if ( wp_http_validate_url( $id ) ) { | |
| 239 | + $id = ''; | |
| 240 | + } | |
| 241 | + | |
| 223 | 242 | $url = parse_url( $url ); |
| 224 | 243 | |
| 225 | 244 | if ( array_key_exists( 'host', $url ) ) { |
| 226 | 245 | if ( 0 === strcasecmp( $url['host'], 'youtu.be' ) ) { |
| 227 | 246 | $id = substr( $url['path'], 1 ); |
| 228 | - } elseif ( 0 === strcasecmp( $url['host'], 'www.youtube.com' ) ) { | |
| 247 | + } elseif ( 0 === strcasecmp( $url['host'], 'www.youtube.com' ) || 0 === strcasecmp( $url['host'], 'youtube.com' ) ) { | |
| 229 | 248 | if ( isset( $url['query'] ) ) { |
| 230 | 249 | parse_str( $url['query'], $url['query'] ); |
| 231 | 250 | |
| 232 | 251 | if ( isset( $url['query']['v'] ) ) { |
| @@ -235,10 +254,9 @@ | ||
| 235 | 254 | } |
| 236 | 255 | |
| 237 | 256 | if ( empty( $id ) ) { |
| 238 | 257 | $url['path'] = explode( '/', substr( $url['path'], 1 ) ); |
| 239 | - | |
| 240 | - if ( in_array( $url['path'][0], array( 'e', 'embed', 'v' ) ) ) { | |
| 258 | + if ( in_array( $url['path'][0], array( 'e', 'embed', 'v', 'shorts', 'live' ) ) ) { | |
| 241 | 259 | $id = $url['path'][1]; |
| 242 | 260 | } |
| 243 | 261 | } |
| 244 | 262 | } |
| @@ -496,9 +514,9 @@ | ||
| 496 | 514 | ), |
| 497 | 515 | $params |
| 498 | 516 | ); |
| 499 | 517 | |
| 500 | - $api_response = $this->request_api( $api_url, $api_params ); | |
| 518 | + $api_response = $this->request_api( $api_url, $api_params, 'live' ); | |
| 501 | 519 | if ( isset( $api_response->error ) ) { |
| 502 | 520 | return $api_response; |
| 503 | 521 | } |
| 504 | 522 | |
| @@ -580,12 +598,9 @@ | ||
| 580 | 598 | $current_page = isset( $params['pageToken'] ) ? (int) $params['pageToken'] : 1; |
| 581 | 599 | $current_page = max( $current_page, 1 ); |
| 582 | 600 | $current_page = min( $current_page, $total_pages ); |
| 583 | 601 | |
| 584 | - $offset = ( $current_page - 1 ) * $params['maxResults']; | |
| 585 | - if ( $offset < 0 ) { | |
| 586 | - $offset = 0; | |
| 587 | - } | |
| 602 | + $offset = max( 0, ( $current_page - 1 ) * $params['maxResults'] ); | |
| 588 | 603 | |
| 589 | 604 | $current_ids = array_slice( $all_ids, $offset, $params['maxResults'] ); |
| 590 | 605 | $params['id'] = implode( ',', $current_ids ); |
| 591 | 606 | |
| @@ -612,9 +627,11 @@ | ||
| 612 | 627 | $response = new stdClass(); |
| 613 | 628 | $response->videos = $videos; |
| 614 | 629 | |
| 615 | 630 | $response->page_info = array( |
| 616 | - 'videos_found' => $total_videos | |
| 631 | + 'videos_found' => $total_videos, | |
| 632 | + 'total_pages' => $total_pages, | |
| 633 | + 'paged' => $current_page | |
| 617 | 634 | ); |
| 618 | 635 | |
| 619 | 636 | if ( $current_page > 1 ) { |
| 620 | 637 | $response->page_info['prev_page_token'] = $current_page - 1; |
| @@ -625,8 +642,96 @@ | ||
| 625 | 642 | } |
| 626 | 643 | |
| 627 | 644 | return $response; |
| 628 | 645 | } |
| 646 | + | |
| 647 | + /** | |
| 648 | + * Get videos from our custom database table "{$wpdb->prefix}ayg_videos". | |
| 649 | + * | |
| 650 | + * @since 2.5.7 | |
| 651 | + * @access private | |
| 652 | + * @param array $params Array of query params. | |
| 653 | + * @return stdClass | |
| 654 | + */ | |
| 655 | + private function get_videos_from_db( $params = array() ) { | |
| 656 | + global $wpdb; | |
| 657 | + | |
| 658 | + $videos_table = $wpdb->prefix . 'ayg_videos'; | |
| 659 | + $galleries_table = $wpdb->prefix . 'ayg_galleries'; | |
| 660 | + | |
| 661 | + $search_term = '%' . $wpdb->esc_like( $params['searchTerm'] ) . '%'; | |
| 662 | + $gallery_id = $params['uid']; | |
| 663 | + | |
| 664 | + // Get Total Videos Count | |
| 665 | + $total_query = $wpdb->prepare( | |
| 666 | + "SELECT COUNT(*) | |
| 667 | + FROM $videos_table AS v | |
| 668 | + INNER JOIN $galleries_table AS g ON v.id = g.video_id | |
| 669 | + WHERE g.gallery_id = %s | |
| 670 | + AND (v.title LIKE %s OR v.description LIKE %s)", | |
| 671 | + $gallery_id, $search_term, $search_term | |
| 672 | + ); | |
| 673 | + | |
| 674 | + $total_videos = $wpdb->get_var( $total_query ); | |
| 675 | + | |
| 676 | + if ( empty( $total_videos ) ) { | |
| 677 | + return $this->get_error( __( 'No videos found matching your query.', 'automatic-youtube-gallery' ) ); | |
| 678 | + } | |
| 679 | + | |
| 680 | + // Fetch Paginated Videos | |
| 681 | + $limit = $params['maxResults']; | |
| 682 | + | |
| 683 | + $total_pages = ceil( $total_videos / $limit ); | |
| 684 | + | |
| 685 | + $current_page = isset( $params['pageToken'] ) ? (int) $params['pageToken'] : 1; | |
| 686 | + $current_page = max( $current_page, 1 ); | |
| 687 | + $current_page = min( $current_page, $total_pages ); | |
| 688 | + | |
| 689 | + $offset = max( 0, ( $current_page - 1 ) * $limit ); | |
| 690 | + | |
| 691 | + $query = $wpdb->prepare( | |
| 692 | + "SELECT v.* | |
| 693 | + FROM $videos_table AS v | |
| 694 | + INNER JOIN $galleries_table AS g ON v.id = g.video_id | |
| 695 | + WHERE g.gallery_id = %s | |
| 696 | + AND (v.title LIKE %s OR v.description LIKE %s) | |
| 697 | + ORDER BY v.published_at_datetime DESC | |
| 698 | + LIMIT %d OFFSET %d", | |
| 699 | + $gallery_id, $search_term, $search_term, $limit, $offset | |
| 700 | + ); | |
| 701 | + | |
| 702 | + $videos = $wpdb->get_results( $query ); | |
| 703 | + | |
| 704 | + if ( empty( $videos ) ) { | |
| 705 | + return $this->get_error( __( 'No videos found matching your query.', 'automatic-youtube-gallery' ) ); | |
| 706 | + } | |
| 707 | + | |
| 708 | + foreach ( $videos as $index => $video ) { | |
| 709 | + if ( ! empty( $video->thumbnails ) ) { | |
| 710 | + $videos[ $index ]->thumbnails = maybe_unserialize( $video->thumbnails ); | |
| 711 | + } | |
| 712 | + } | |
| 713 | + | |
| 714 | + // Process output | |
| 715 | + $response = new stdClass(); | |
| 716 | + $response->videos = $videos; | |
| 717 | + | |
| 718 | + $response->page_info = array( | |
| 719 | + 'videos_found' => $total_videos, | |
| 720 | + 'total_pages' => $total_pages, | |
| 721 | + 'paged' => $current_page | |
| 722 | + ); | |
| 723 | + | |
| 724 | + if ( $current_page > 1 ) { | |
| 725 | + $response->page_info['prev_page_token'] = $current_page - 1; | |
| 726 | + } | |
| 727 | + | |
| 728 | + if ( $current_page < $total_pages ) { | |
| 729 | + $response->page_info['next_page_token'] = $current_page + 1; | |
| 730 | + } | |
| 731 | + | |
| 732 | + return $response; | |
| 733 | + } | |
| 629 | 734 | |
| 630 | 735 | /** |
| 631 | 736 | * Get API URL by request. |
| 632 | 737 | * |
| @@ -645,31 +750,21 @@ | ||
| 645 | 750 | * @since 1.0.0 |
| 646 | 751 | * @access private |
| 647 | 752 | * @param string $url YouTube API URL. |
| 648 | 753 | * @param array $params Array of query params. |
| 649 | - * @param string $context "channel_id", "playlist_id", or "videos" | |
| 754 | + * @param string $context "channel_id", "playlist_id", "videos", or "live" | |
| 650 | 755 | * @return mixed |
| 651 | 756 | */ |
| 652 | 757 | private function request_api( $url, $params, $context = 'videos' ) { |
| 653 | 758 | $params['key'] = $this->api_key; |
| 654 | - | |
| 655 | - // Request from cache | |
| 656 | - if ( ! $this->is_development_mode ) { | |
| 657 | - $cache_url = $url . ( strpos( $url, '?' ) === false ? '?' : '' ) . http_build_query( $params ); | |
| 658 | - $cache_key = 'ayg_' . md5( $cache_url ); | |
| 659 | - $cache_data = get_transient( $cache_key ); | |
| 660 | - | |
| 661 | - if ( ! empty( $cache_data ) ) { | |
| 662 | - return $cache_data; | |
| 663 | - } | |
| 664 | - } | |
| 665 | - | |
| 666 | - // Request from API | |
| 759 | + | |
| 760 | + // Build API URL | |
| 667 | 761 | $cache_duration = 0; |
| 668 | 762 | if ( isset( $params['cache'] ) ) { |
| 669 | 763 | $cache_duration = (int) $params['cache']; |
| 670 | 764 | unset( $params['cache'] ); |
| 671 | 765 | } |
| 766 | + $cache_duration = min( $cache_duration, 2419200 ); // Max cache duration: 1 Month | |
| 672 | 767 | |
| 673 | 768 | $q = ''; |
| 674 | 769 | if ( isset( $params['q'] ) ) { |
| 675 | 770 | $q = $params['q']; |
| @@ -675,15 +770,29 @@ | ||
| 675 | 770 | $q = $params['q']; |
| 676 | 771 | unset( $params['q'] ); |
| 677 | 772 | } |
| 678 | 773 | |
| 679 | - $url = $url . ( strpos( $url, '?' ) === false ? '?' : '' ) . http_build_query( $params ); | |
| 774 | + $api_url = $url . ( strpos( $url, '?' ) === false ? '?' : '&' ) . http_build_query( $params ); | |
| 680 | 775 | if ( ! empty( $q ) ) { |
| 681 | - $url .= '&q=' . $q; | |
| 776 | + $api_url .= '&q=' . $q; | |
| 682 | 777 | } |
| 683 | 778 | |
| 684 | - $request = wp_remote_get( $url, array( | |
| 779 | + // Request from cache | |
| 780 | + if ( ! $this->is_development_mode && $cache_duration > 0 ) { | |
| 781 | + $cache_key = 'ayg_' . md5( $api_url ); | |
| 782 | + $cache_data = get_transient( $cache_key ); | |
| 783 | + | |
| 784 | + if ( ! empty( $cache_data ) ) { | |
| 785 | + return $cache_data; | |
| 786 | + } | |
| 787 | + } | |
| 788 | + | |
| 789 | + // Request from API | |
| 790 | + $timeout = apply_filters( 'ayg_api_request_timeout', 15 ); | |
| 791 | + | |
| 792 | + $request = wp_remote_get( $api_url, array( | |
| 685 | 793 | 'headers' => [ 'referer' => home_url() ], |
| 794 | + 'timeout' => $timeout, // Increase timeout if needed | |
| 686 | 795 | ) ); |
| 687 | 796 | |
| 688 | 797 | if ( is_wp_error( $request ) ) { |
| 689 | 798 | return $this->get_error( $request->get_error_message() ); |
| @@ -689,10 +798,13 @@ | ||
| 689 | 798 | return $this->get_error( $request->get_error_message() ); |
| 690 | 799 | } |
| 691 | 800 | |
| 692 | 801 | $body = wp_remote_retrieve_body( $request ); |
| 802 | + $data = json_decode( $body ); | |
| 693 | 803 | |
| 694 | - $data = json_decode( $body ); | |
| 804 | + if ( empty( $data ) ) { | |
| 805 | + return $this->get_error( __( 'Empty or invalid API response', 'automatic-youtube-gallery' ) ); | |
| 806 | + } | |
| 695 | 807 | |
| 696 | 808 | if ( isset( $data->error ) ) { |
| 697 | 809 | $message = "Error " . $data->error->code . " " . $data->error->message; |
| 698 | 810 | |
| @@ -703,30 +815,42 @@ | ||
| 703 | 815 | return $this->get_error( $message ); |
| 704 | 816 | } |
| 705 | 817 | |
| 706 | 818 | // Store in cache (transients) |
| 707 | - if ( ! $this->is_development_mode && 'videos' == $context && $cache_duration > 0 ) { | |
| 708 | - if ( isset( $data->items ) && is_array( $data->items ) && count( $data->items ) > 0 ) { | |
| 709 | - set_transient( $cache_key, $data, $cache_duration ); | |
| 819 | + $cache_enabled = false; | |
| 710 | 820 | |
| 711 | - // Get the current list of transients | |
| 712 | - $cache_keys = get_option( 'ayg_transient_keys', array() ); | |
| 713 | - if ( ! is_array( $cache_keys ) ) { | |
| 714 | - $cache_keys = (array) $cache_keys; | |
| 821 | + if ( ! $this->is_development_mode && $cache_duration > 0 ) { | |
| 822 | + if ( 'videos' === $context ) { | |
| 823 | + if ( ! empty( $data->items ) && is_array( $data->items ) ) { | |
| 824 | + $cache_enabled = true; | |
| 715 | 825 | } |
| 826 | + } | |
| 716 | 827 | |
| 717 | - // Append our new one | |
| 718 | - if ( ! in_array( $cache_key, $cache_keys ) ) { | |
| 719 | - $cache_keys[] = $cache_key; | |
| 720 | - } | |
| 828 | + if ( 'live' === $context ) { | |
| 829 | + $cache_enabled = true; | |
| 830 | + } | |
| 831 | + } | |
| 721 | 832 | |
| 722 | - // Save it to the DB | |
| 723 | - update_option( 'ayg_transient_keys', $cache_keys ); | |
| 833 | + if ( $cache_enabled ) { | |
| 834 | + set_transient( $cache_key, $data, $cache_duration ); | |
| 835 | + | |
| 836 | + // Get the current list of transients | |
| 837 | + $cache_keys = get_option( 'ayg_transient_keys', array() ); | |
| 838 | + if ( ! is_array( $cache_keys ) ) { | |
| 839 | + $cache_keys = (array) $cache_keys; | |
| 724 | 840 | } |
| 841 | + | |
| 842 | + // Append our new one | |
| 843 | + if ( ! in_array( $cache_key, $cache_keys ) ) { | |
| 844 | + $cache_keys[] = $cache_key; | |
| 845 | + } | |
| 846 | + | |
| 847 | + // Save it to the DB | |
| 848 | + update_option( 'ayg_transient_keys', $cache_keys ); | |
| 725 | 849 | } |
| 726 | 850 | |
| 727 | 851 | // Store videos in our custom database table "{$wpdb->prefix}ayg_videos" |
| 728 | - ayg_db_store_videos( $data ); | |
| 852 | + ayg_db_store_videos( $data, $this->params ); | |
| 729 | 853 | |
| 730 | 854 | // Finally return the data |
| 731 | 855 | return $data; |
| 732 | 856 | } |
| @@ -739,17 +863,13 @@ | ||
| 739 | 863 | * @param object $data YouTube API response object. |
| 740 | 864 | * @return mixed |
| 741 | 865 | */ |
| 742 | 866 | private function parse_videos( $data ) { |
| 743 | - if ( ! isset( $data->items ) ) { | |
| 867 | + if ( empty( $data->items ) || ! is_array( $data->items ) ) { | |
| 744 | 868 | return $this->get_error( __( 'No videos found matching your query.', 'automatic-youtube-gallery' ) ); |
| 745 | 869 | } |
| 746 | 870 | |
| 747 | - $items = $data->items; | |
| 748 | - if ( ! is_array( $items ) || 0 == count( $items ) ) { | |
| 749 | - return $this->get_error( __( 'No videos found matching your query.', 'automatic-youtube-gallery' ) ); | |
| 750 | - } | |
| 751 | - | |
| 871 | + $items = $data->items; | |
| 752 | 872 | $videos = array(); |
| 753 | 873 | |
| 754 | 874 | foreach ( $items as $item ) { |
| 755 | 875 | $video = new stdClass(); |
| @@ -823,14 +943,26 @@ | ||
| 823 | 943 | * @param object $data YouTube API response object. |
| 824 | 944 | * @return array |
| 825 | 945 | */ |
| 826 | 946 | private function parse_page_info( $data ) { |
| 827 | - $page_info = array(); | |
| 947 | + $page_info = array( | |
| 948 | + 'videos_found' => 0 | |
| 949 | + ); | |
| 828 | 950 | |
| 829 | - // Total count of videos found | |
| 951 | + // Total number of videos found | |
| 830 | 952 | if ( isset( $data->pageInfo ) && isset( $data->pageInfo->totalResults ) ) { |
| 831 | - $page_info['videos_found'] = $data->pageInfo->totalResults; | |
| 832 | - } | |
| 953 | + $page_info['videos_found'] = (int) $data->pageInfo->totalResults; | |
| 954 | + } | |
| 955 | + | |
| 956 | + // Calculate total number of pages | |
| 957 | + if ( $page_info['videos_found'] > 0 ) { | |
| 958 | + if ( 'search' == $this->params['type'] ) { | |
| 959 | + $limit = min( (int) $this->params['limit'], $page_info['videos_found'] ); | |
| 960 | + $page_info['total_pages'] = ceil( $limit / (int) $this->params['maxResults'] ); | |
| 961 | + } else { | |
| 962 | + $page_info['total_pages'] = ceil( $page_info['videos_found'] / (int) $this->params['maxResults'] ); | |
| 963 | + } | |
| 964 | + } | |
| 833 | 965 | |
| 834 | 966 | // Token for the previous page |
| 835 | 967 | if ( isset( $data->prevPageToken ) ) { |
| 836 | 968 | $page_info['prev_page_token'] = $data->prevPageToken; |