Files
ytmusicapi-perl/YTMusicAPI/Mixins/SearchMixin.pm
2024-03-24 18:17:49 +01:00

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;