package YTMusicAPI::YTMusic; use strict; use warnings; use Moose; with 'YTMusicAPI::Mixins::SearchMixin'; use JSON; use LWP::UserAgent; use HTTP::Cookies; use URI::Escape; use Encode qw(encode_utf8); use Data::Dumper; use YTMusicAPI::Constants qw( $SUPPORTED_LANGUAGES $SUPPORTED_LOCATIONS USER_AGENT YTM_BASE_API YTM_DOMAIN YTM_PARAMS YTM_PARAMS_KEY ); use YTMusicAPI::Helpers; use YTMusicAPI::Parsers::Parser; use YTMusicAPI::Auth::AuthTypes; sub new { my ( $class, $args ) = @_; my $self = { auth => $args->{auth} // undef, user => $args->{user} // undef, requests_session => $args->{requests_session} // 1, proxies => $args->{proxies} // undef, language => $args->{language} // 'en', location => $args->{location} // '', oauth_credentials => $args->{oauth_credentials} // undef, _base_headers => undef, _headers => undef, _input_dict => undef, _token => undef, _session => undef, _proxies => undef, }; bless $self, $class; $self->{auth_type} = AuthType::UNAUTHORIZED; if ( UNIVERSAL::isa( $self->{requests_session}, "LWP::UserAgent" ) ) { $self->{_session} = $self->{requests_session}; } elsif ( $self->{requests_session} ) { $self->{_session} = LWP::UserAgent->new; } $self->{cookies} = "SOCS=CAI"; $self->{context} = initialize_context(); $self->{context}{"context"}{"client"}{"hl"} = "en"; $self->{parser} = YTMusicAPI::Parsers::Parser->new(); if ( $self->{user} ) { $self->{context}{'context'}{'user'}{'onBehalfOfUser'} = $self->{user}; } $self->{params} = YTM_PARAMS; if ( $self->{auth_type} == AuthType::BROWSER ) { $self->{params} .= YTM_PARAMS_KEY; } return $self; } sub base_headers { my ($self) = @_; if ( !$self->{_base_headers} ) { if ( $self->{auth_type} == AuthType::BROWSER or $self->{auth_type} == AuthType::OAUTH_CUSTOM_FULL ) { $self->{_base_headers} = $self->{_input_dict}; } else { $self->{_base_headers} = { "user-agent" => USER_AGENT, "accept" => "*/*", "accept-encoding" => "gzip, deflate", "content-type" => "application/json", "content-encoding" => "gzip", "origin" => YTM_DOMAIN, }; } } return $self->{_base_headers}; } sub headers { my ($self) = @_; if ( !$self->{_headers} ) { $self->{_headers} = $self->base_headers(); } if ( $self->{auth_type} == AuthType::BROWSER ) { $self->{_headers}{'authorization'} = get_authorization( $self->{sapisid} . ' ' . $self->{origin} ); } elsif ( $self->{auth_type} != AuthType::OAUTH_CUSTOM_FULL ) { # $self->{_headers}{'authorization'} = $self->{_token}->as_auth(); $self->{_headers}{'X-Goog-Request-Time'} = '' . time(); } return $self->{_headers}; } sub _send_request { my ( $self, $url, $body, $additionalParams ) = @_; $additionalParams //= ""; @$body{ keys %{ $self->{context} } } = values %{ $self->{context} }; if ( $self->{_headers} and !exists $self->{_headers}{'X-Goog-Visitor-Id'} ) { my $visitor_id = get_visitor_id( $self->{_send_get_request} ); $self->{_headers}{'X-Goog-Visitor-Id'} = %$visitor_id; } $self->{_headers}{'Cookie'} = $self->{cookies}; my $request = HTTP::Request->new( POST => YTM_BASE_API . $url . $self->{params} . $additionalParams ); my $headers = $self->headers(); foreach my $header_name ( keys %$headers ) { $request->header( $header_name => $headers->{$header_name} ); } $request->content( encode_json($body) ); my $response = $self->{_session}->request($request); if ( !$response->is_success ) { my $message = "Server returned HTTP " . $response->code . ".\n"; my $error = $response->message; die $message . $error; } return decode_json( $response->decoded_content ); } sub _send_get_request { my ( $self, $url, $params ) = @_; my $response = $self->{_session} ->get( $url, $self->{_headers} ? $self->headers() : $self->base_headers(), ); return $response; } sub _check_auth { my ($self) = @_; unless ( $self->{auth} ) { die "Please provide authentication before using this function"; } } 1;