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

156 lines
3.9 KiB
Perl

package YTMusicAPI::Parsers::Songs;
use strict;
use warnings;
use Exporter 'import';
use Regexp::Common qw(number);
use YTMusicAPI::Parsers::Utils;
use YTMusicAPI::Navigation;
sub parse_song_artists {
my ( $data, $index ) = @_;
my $flex_item = get_flex_column_item( $data, $index );
if ( !$flex_item ) {
return undef;
}
else {
my $runs = $flex_item->{"text"}{"runs"};
return parse_song_artists_runs($runs);
}
}
sub parse_song_artists_runs {
my ($runs) = @_;
my @artists = ();
for my $j ( 0 .. int( @$runs / 2 ) ) {
push @artists,
{
"name" => $runs->[ $j * 2 ]{"text"},
"id" => nav( $runs->[ $j * 2 ], $NAVIGATION_BROWSE_ID, 1 )
};
}
return \@artists;
}
sub parse_song_runs {
my ($runs) = @_;
my %parsed = ( "artists" => [] );
my $i = 0;
for my $run (@$runs) {
if ( $i % 2 ) { # uneven items are always separators
$i++;
next;
}
my $text = $run->{"text"};
if ( exists $run->{"navigationEndpoint"} ) { # artist or album
my $item = {
"name" => $text,
"id" => nav( $run, $NAVIGATION_BROWSE_ID, 1 )
};
if (
$item->{"id"}
&& ( $item->{"id"} =~ /^MPRE/
|| $item->{"id"} =~ /release_detail/ )
)
{ # album
$parsed{"album"} = $item;
}
else { # artist
push @{ $parsed{"artists"} }, $item;
}
}
else {
# note: YT uses non-breaking space \xa0 to separate number and magnitude
if ( $text =~ /^\d([^ ])* [^ ]*$/ && $i > 0 ) {
( $parsed{"views"} ) = split( / /, $text, 2 );
}
elsif ( $text =~ /^(\d+:)*\d+:\d+$/ ) {
$parsed{"duration"} = $text;
$parsed{"duration_seconds"} = parse_duration($text);
}
elsif ( $text =~ /^\d{4}$/ ) {
$parsed{"year"} = $text;
}
else { # artist without id
push @{ $parsed{"artists"} },
{ "name" => $text, "id" => undef };
}
}
$i++;
}
return \%parsed;
}
sub parse_song_album {
my ( $data, $index ) = @_;
my $flex_item = get_flex_column_item( $data, $index );
my $browse_id = nav( $flex_item, $TEXT_RUN + $NAVIGATION_BROWSE_ID, 1 );
return !$flex_item
? undef
: { "name" => get_item_text( $data, $index ), "id" => $browse_id };
}
sub parse_song_library_status {
my ($item) = @_;
my $library_status =
nav( $item, [ $TOGGLE_MENU, "defaultIcon", "iconType" ], 1 );
return $library_status eq "LIBRARY_SAVED";
}
sub parse_song_menu_tokens {
my ($item) = @_;
my $toggle_menu = $item->{$TOGGLE_MENU};
my $library_add_token =
nav( $toggle_menu, [ "defaultServiceEndpoint", $FEEDBACK_TOKEN ], 1 );
my $library_remove_token =
nav( $toggle_menu, [ "toggledServiceEndpoint", $FEEDBACK_TOKEN ], 1 );
my $in_library = parse_song_library_status($item);
if ($in_library) {
my $tmp = $library_remove_token;
$library_add_token = $library_remove_token;
$library_remove_token = $tmp;
}
return { "add" => $library_add_token, "remove" => $library_remove_token };
}
sub parse_like_status {
my ($service) = @_;
my @status = ( "LIKE", "INDIFFERENT" );
my $status_index = 0;
for my $i ( 0 .. $#status ) {
if ( $status[$i] eq $service->{"likeEndpoint"}{"status"} ) {
$status_index = $i;
last;
}
}
return $status[ $status_index - 1 ];
}
our @EXPORT = qw(
parse_song_artists
parse_song_artists_runs
parse_song_runs
parse_song_album
parse_song_library_status
parse_song_menu_tokens
parse_like_status
);
1;