Files
ytmusicapi-perl/YTMusicAPI/Continuations.pm
2024-03-24 23:34:26 +01:00

171 lines
4.7 KiB
Perl

package YTMusicAPI::Continuations;
use strict;
use warnings;
use Exporter 'import';
use YTMusicAPI::Navigation;
sub get_continuations {
my ( $results, $continuation_type, $limit, $request_func, $parse_func,
$ctoken_path, $reloadable )
= @_;
$ctoken_path //= "";
$reloadable //= 0;
my @items = ();
while ( exists $results->{"continuations"}
&& ( !defined $limit || scalar(@items) < $limit ) )
{
my $additionalParams =
$reloadable
? get_reloadable_continuation_params($results)
: get_continuation_params( $results, $ctoken_path );
my $response = $request_func->($additionalParams);
if ( exists $response->{"continuationContents"} ) {
$results =
$response->{"continuationContents"}->{$continuation_type};
}
else {
last;
}
my $contents = get_continuation_contents( $results, $parse_func );
last if scalar(@$contents) == 0;
push( @items, @$contents );
}
return \@items;
}
sub get_validated_continuations {
my ( $results, $continuation_type, $limit, $per_page, $request_func,
$parse_func, $ctoken_path )
= @_;
$ctoken_path //= "";
my @items = ();
while ( exists $results->{"continuations"} && scalar(@items) < $limit ) {
my $additionalParams =
get_continuation_params( $results, $ctoken_path );
my $wrapped_parse_func = sub {
my $raw_response = shift;
return get_parsed_continuation_items( $raw_response, $parse_func,
$continuation_type );
};
my $validate_func = sub {
my $parsed = shift;
return validate_response( $parsed, $per_page, $limit,
scalar(@items) );
};
my $response =
resend_request_until_parsed_response_is_valid( $request_func,
$additionalParams, $wrapped_parse_func, $validate_func, 3 );
$results = $response->{"results"};
push( @items, @{ $response->{"parsed"} } );
}
return \@items;
}
sub get_parsed_continuation_items {
my ( $response, $parse_func, $continuation_type ) = @_;
my $results = $response->{"continuationContents"}->{$continuation_type};
return {
"results" => $results,
"parsed" => get_continuation_contents( $results, $parse_func )
};
}
sub get_continuation_params {
my ( $results, $ctoken_path ) = @_;
$ctoken_path //= "";
my $ctoken = nav(
$results,
[
"continuations", 0,
"next" . $ctoken_path . "ContinuationData", "continuation"
]
);
return get_continuation_string($ctoken);
}
sub get_reloadable_continuation_params {
my ($results) = @_;
my $ctoken = nav( $results,
[ "continuations", 0, "reloadContinuationData", "continuation" ] );
return get_continuation_string($ctoken);
}
sub get_continuation_string {
my ($ctoken) = @_;
return "&ctoken=" . $ctoken . "&continuation=" . $ctoken;
}
sub get_continuation_contents {
my ( $continuation, $parse_func ) = @_;
foreach my $term ( "contents", "items" ) {
if ( exists $continuation->{$term} ) {
return $parse_func->( $continuation->{$term} );
}
}
return [];
}
sub resend_request_until_parsed_response_is_valid {
my ( $request_func, $request_additional_params, $parse_func,
$validate_func, $max_retries )
= @_;
my $response = $request_func->($request_additional_params);
my $parsed_object = $parse_func->($response);
my $retry_counter = 0;
while ( !$validate_func->($parsed_object) && $retry_counter < $max_retries )
{
$response = $request_func->($request_additional_params);
my $attempt = $parse_func->($response);
if (
scalar( @{ $attempt->{"parsed"} } ) >
scalar( @{ $parsed_object->{"parsed"} } ) )
{
$parsed_object = $attempt;
}
$retry_counter++;
}
return $parsed_object;
}
sub validate_response {
my ( $response, $per_page, $limit, $current_count ) = @_;
my $remaining_items_count = $limit - $current_count;
my $expected_items_count =
$per_page < $remaining_items_count ? $per_page : $remaining_items_count;
# response is invalid, if it has less items than the minimal expected count
return scalar( @{ $response->{"parsed"} } ) >= $expected_items_count;
}
our @EXPORT = qw(
get_continuations
get_validated_continuations
get_parsed_continuation_items
get_continuation_params
get_reloadable_continuation_params
get_continuation_string
get_continuation_contents
resend_request_until_parsed_response_is_valid
validate_response
);
1;