178 lines
5.6 KiB
Perl
178 lines
5.6 KiB
Perl
package YTMusicAPI::Mixins::SearchMixin;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Moose::Role;
|
|
use Data::Dumper;
|
|
|
|
use YTMusicAPI::Helpers;
|
|
use YTMusicAPI::Navigation;
|
|
use YTMusicAPI::Parsers::Search;
|
|
|
|
sub search {
|
|
my ( $self, $query, $filter, $scope, $limit, $ignore_spelling ) = @_;
|
|
$filter //= undef;
|
|
$scope //= undef;
|
|
$limit //= 20;
|
|
$ignore_spelling //= 0;
|
|
|
|
my $body = { 'query' => $query };
|
|
my $endpoint = "search";
|
|
my $search_results = [];
|
|
my $filters = [
|
|
"albums", "artists",
|
|
"playlists", "community_playlists",
|
|
"featured_playlists", "songs",
|
|
"videos", "profiles",
|
|
"podcasts", "episodes",
|
|
];
|
|
|
|
if ( $filter and !grep { $_ eq $filter } @$filters ) {
|
|
die "Invalid filter provided. "
|
|
. "Please use one of the following filters or leave out the parameter: "
|
|
. join( ", ", @$filters );
|
|
}
|
|
|
|
my $scopes = [ "library", "uploads" ];
|
|
if ( $scope and !grep { $_ eq $scope } @$scopes ) {
|
|
die "Invalid scope provided. "
|
|
. "Please use one of the following scopes or leave out the parameter: "
|
|
. join( ", ", @$scopes );
|
|
}
|
|
|
|
if ( $scope and $scope eq @$scopes[1] and $filter ) {
|
|
die "No filter can be set when searching uploads. "
|
|
. "Please unset the filter parameter when scope is set to uploads.";
|
|
}
|
|
|
|
if ( $scope
|
|
and $scope eq @$scopes[0]
|
|
and grep { $_ eq $filter } @$filters[ 3 .. 4 ] )
|
|
{
|
|
die "$filter cannot be set when searching library. "
|
|
. "Please use one of the following filters or leave out the parameter: "
|
|
. join( ", ", ( @$filters[ 0 .. 2 ], @$filters[ 5 .. $#$filters ] ) );
|
|
}
|
|
|
|
my $params = get_search_params( $filter, $scope, $ignore_spelling );
|
|
if ($params) {
|
|
$body->{"params"} = $params;
|
|
}
|
|
|
|
my $response = $self->_send_request( $endpoint, $body );
|
|
|
|
if ( !exists $response->{"contents"} ) {
|
|
return $search_results;
|
|
}
|
|
|
|
my $results;
|
|
|
|
if ( exists $response->{"contents"}{"tabbedSearchResultsRenderer"} ) {
|
|
my $tab_index =
|
|
!( $scope or $filter ) ? 0 : array_index( $scopes, $scope ) + 1;
|
|
$results =
|
|
$response->{"contents"}{"tabbedSearchResultsRenderer"}{"tabs"}
|
|
[$tab_index]{"tabRenderer"}{"content"};
|
|
}
|
|
else {
|
|
$results = $response->{"contents"};
|
|
}
|
|
|
|
$results = nav( $results, $SECTION_LIST );
|
|
|
|
if ( scalar @{$results} == 1
|
|
and exists $results->[0]->{"itemSectionRenderer"} )
|
|
{
|
|
return $search_results;
|
|
}
|
|
|
|
if ( $filter and index( $filter, "playlists" ) != -1 ) {
|
|
$filter = "playlists";
|
|
}
|
|
elsif ( $scope and $scope == $scopes->[1] ) {
|
|
$filter = $scopes->[1];
|
|
}
|
|
|
|
foreach my $res ( @{$results} ) {
|
|
my $category = undef;
|
|
my $type = undef;
|
|
if ( exists $res->{"musicCardShelfRenderer"} ) {
|
|
my $top_result = parse_top_result( $res->{"musicCardShelfRenderer"},
|
|
$self->{parser}->get_search_result_types() );
|
|
push( @$search_results, $top_result );
|
|
if ( $results =
|
|
nav( $res, [ "musicCardShelfRenderer", "contents" ], 1 ) )
|
|
{
|
|
# category "more from youtube" is missing sometimes
|
|
if ( exists $results->[0]->{"messageRenderer"} ) {
|
|
$category = nav( shift(@$results),
|
|
[ "messageRenderer", @$TEXT_RUN_TEXT ] );
|
|
}
|
|
}
|
|
else {
|
|
next;
|
|
}
|
|
}
|
|
elsif ( exists $res->{"musicShelfRenderer"} ) {
|
|
$results = $res->{"musicShelfRenderer"}{"contents"};
|
|
my $type_filter = $filter;
|
|
$category = nav( $res, [ @$MUSIC_SHELF, @$TITLE_TEXT ], 1 );
|
|
if ( !$type_filter and $scope and $scope eq $scopes->[0] ) {
|
|
$type_filter = $category;
|
|
}
|
|
$type = substr( $type_filter, 0, -1 ) if $type_filter;
|
|
$type = lc($type) if $type;
|
|
}
|
|
else {
|
|
next;
|
|
}
|
|
my $search_result_types = $self->{parser}->get_search_result_types();
|
|
push(
|
|
@$search_results,
|
|
parse_search_results(
|
|
$results, $search_result_types, $type, $category
|
|
)
|
|
);
|
|
if ($filter) { # if filter is set, there are continuations
|
|
my $request_func = sub {
|
|
my ($additionalParams) = @_;
|
|
return $self->_send_request( $endpoint, $body,
|
|
$additionalParams );
|
|
};
|
|
my $parse_func = sub {
|
|
my ($contents) = @_;
|
|
return parse_search_results( $contents, $search_result_types,
|
|
$type, $category );
|
|
};
|
|
push(
|
|
@$search_results,
|
|
get_continuations(
|
|
$res->{"musicShelfRenderer"},
|
|
"musicShelfContinuation",
|
|
$limit - scalar(@$search_results),
|
|
$request_func,
|
|
$parse_func
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
return $search_results;
|
|
}
|
|
|
|
sub get_search_suggestions {
|
|
my ( $self, $query, $detailed_runs ) = @_;
|
|
|
|
$detailed_runs //= 0;
|
|
|
|
my %body = ( "input" => $query );
|
|
my $endpoint = "music/get_search_suggestions";
|
|
my $response = $self->_send_request( $endpoint, %body );
|
|
my $search_suggestions =
|
|
parse_search_suggestions( $response, $detailed_runs );
|
|
return $search_suggestions;
|
|
}
|
|
|
|
1;
|