Format files
All checks were successful
release / release-plugins (push) Successful in 25s

This commit is contained in:
mschuepbach
2024-03-27 16:24:10 +01:00
parent 2c941230a5
commit 128f6ca6f0
5 changed files with 354 additions and 277 deletions

View File

@@ -2,16 +2,17 @@ package Plugins::YouTubeMusic::API;
use strict; use strict;
use Digest::MD5 qw(md5_hex); use Digest::MD5 qw(md5_hex);
use File::Spec::Functions qw(catdir); use File::Spec::Functions qw(catdir);
use JSON::XS::VersionOneAndTwo; use JSON::XS::VersionOneAndTwo;
use List::Util qw(min max); use List::Util qw(min max);
use URI::Escape qw(uri_escape uri_escape_utf8); use URI::Escape qw(uri_escape uri_escape_utf8);
use MIME::Base64 qw(decode_base64); use MIME::Base64 qw(decode_base64);
use constant API_URL => 'https://music.youtube.com/youtubei/v1/'; use constant API_URL => 'https://music.youtube.com/youtubei/v1/';
use constant DEFAULT_CACHE_TTL => 24 * 3600; use constant DEFAULT_CACHE_TTL => 24 * 3600;
use constant DEFAULT_API_KEY => 'QUl6YVN5Qi1wd1B0RGt4RjZKUW1BOHFxOWgxbWQ2ME15STVRNWlB'; use constant DEFAULT_API_KEY =>
'QUl6YVN5Qi1wd1B0RGt4RjZKUW1BOHFxOWgxbWQ2ME15STVRNWlB';
use Slim::Utils::Cache; use Slim::Utils::Cache;
use Slim::Utils::Log; use Slim::Utils::Log;
@@ -24,156 +25,198 @@ my $cache = Slim::Utils::Cache->new();
sub flushCache { $cache->cleanup(); } sub flushCache { $cache->cleanup(); }
sub search { sub search {
my ( $class, $cb, $args ) = @_; my ( $class, $cb, $args ) = @_;
$args ||= {}; $args ||= {};
$args->{part} = 'snippet'; $args->{part} = 'snippet';
$args->{type} ||= 'video'; $args->{type} ||= 'video';
$args->{order} ||= $prefs->get('search_rank'); $args->{order} ||= $prefs->get('search_rank');
$args->{relevanceLanguage} = Slim::Utils::Strings::getLanguage(); $args->{relevanceLanguage} = Slim::Utils::Strings::getLanguage();
_pagedCall('search', $args, $cb); _pagedCall( 'search', $args, $cb );
} }
sub searchDirect { sub searchDirect {
my ( $class, $type, $cb, $args ) = @_; my ( $class, $type, $cb, $args ) = @_;
$args ||= {}; $args ||= {};
$args->{_noRegion} = 1; $args->{_noRegion} = 1;
_pagedCall( $type, $args, $cb); _pagedCall( $type, $args, $cb );
} }
sub getCategories { sub getCategories {
my ( $class, $type, $cb, $args ) = @_; my ( $class, $type, $cb, $args ) = @_;
$args ||= {}; $args ||= {};
$args->{hl} = Slim::Utils::Strings::getLanguage(); $args->{hl} = Slim::Utils::Strings::getLanguage();
$args->{_cache_ttl} = 7 * 86400; $args->{_cache_ttl} = 7 * 86400;
_pagedCall($type, $args, $cb); _pagedCall( $type, $args, $cb );
} }
sub getVideoDetails { sub getVideoDetails {
my ( $class, $cb, $ids ) = @_; my ( $class, $cb, $ids ) = @_;
_call('videos', { _call(
part => 'snippet,contentDetails', 'videos',
id => $ids, {
# cache video details a bit longer part => 'snippet,contentDetails',
_cache_ttl => 7 * 86400, id => $ids,
}, $cb);
# cache video details a bit longer
_cache_ttl => 7 * 86400,
},
$cb
);
} }
sub _pagedCall { sub _pagedCall {
my ( $method, $args, $cb ) = @_; my ( $method, $args, $cb ) = @_;
my $wantedItems = $args->{_quantity} || $prefs->get('max_items'); my $wantedItems = $args->{_quantity} || $prefs->get('max_items');
# ignore $args->{_index} and let LMS handle offset
my $wantedIndex = 0;
my @items;
my $pagingCb;
my $pageIndex = 0;
# we want them starting from current index
$wantedItems += $args->{_index} || 0;
main::INFOLOG && $log->info("Searching by [$args->{order}]"); # ignore $args->{_index} and let LMS handle offset
main::INFOLOG && $log->info("Querying [$args->{_quantity}] from [$args->{_index}] to [", $wantedItems-1, "] using [$method]"); my $wantedIndex = 0;
my @items;
my $pagingCb;
my $pageIndex = 0;
# doing a search with a display order, so need to give precedence to 'query_size' # we want them starting from current index
if ($prefs->get('search_sort') || $prefs->get('channel_sort') || $prefs->get('playlist_sort')) { $wantedItems += $args->{_index} || 0;
$wantedItems = (int(($wantedItems - 1) / $prefs->get('query_size')) + 1) * $prefs->get('query_size');
main::INFOLOG && $log->info("Stretching quantity to [$wantedItems] due to sorting");
}
# that the maximum we'll get anyway
$wantedItems = $prefs->get('max_items') if $wantedItems > $prefs->get('max_items');
$pagingCb = sub { main::INFOLOG && $log->info("Searching by [$args->{order}]");
my $results = shift; main::INFOLOG && $log->info(
"Querying [$args->{_quantity}] from [$args->{_index}] to [",
$wantedItems - 1,
"] using [$method]"
);
if ( $results->{error} || !$results->{items} ) { # doing a search with a display order, so need to give precedence to 'query_size'
$log->error("no results"); if ( $prefs->get('search_sort')
$cb->( { items => undef, total => 0 } ) if ( $results->{error} ); || $prefs->get('channel_sort')
return; || $prefs->get('playlist_sort') )
} {
$wantedItems =
( int( ( $wantedItems - 1 ) / $prefs->get('query_size') ) + 1 ) *
$prefs->get('query_size');
main::INFOLOG
&& $log->info("Stretching quantity to [$wantedItems] due to sorting");
}
push @items, @{$results->{items}}; # that the maximum we'll get anyway
$pageIndex += scalar @{$results->{items}}; $wantedItems = $prefs->get('max_items')
if $wantedItems > $prefs->get('max_items');
main::INFOLOG && $log->info("Want $wantedItems items from offset ", $wantedIndex, ", have " . scalar @items . " so far [acquired $pageIndex]"); $pagingCb = sub {
my $results = shift;
if (@items < $wantedItems && $results->{nextPageToken}) { if ( $results->{error} || !$results->{items} ) {
$args->{pageToken} = $results->{nextPageToken}; $log->error("no results");
main::INFOLOG && $log->info("Get next page using token " . $args->{pageToken}); $cb->( { items => undef, total => 0 } ) if ( $results->{error} );
_call($method, $args, $pagingCb); return;
} else { }
my $total = min($results->{'pageInfo'}->{'totalResults'} || $pageIndex, $prefs->get('max_items'));
main::INFOLOG && $log->info("Got all we wanted, return " . scalar @items . "/$total. (YT total ", $results->{'pageInfo'}->{'totalResults'} || 'N/A', ")");
$cb->( { items => \@items, offset => $wantedIndex, total => $total } );
}
};
_call($method, $args, $pagingCb); push @items, @{ $results->{items} };
$pageIndex += scalar @{ $results->{items} };
main::INFOLOG && $log->info( "Want $wantedItems items from offset ",
$wantedIndex,
", have " . scalar @items . " so far [acquired $pageIndex]" );
if ( @items < $wantedItems && $results->{nextPageToken} ) {
$args->{pageToken} = $results->{nextPageToken};
main::INFOLOG
&& $log->info(
"Get next page using token " . $args->{pageToken} );
_call( $method, $args, $pagingCb );
}
else {
my $total =
min( $results->{'pageInfo'}->{'totalResults'} || $pageIndex,
$prefs->get('max_items') );
main::INFOLOG && $log->info(
"Got all we wanted, return "
. scalar @items
. "/$total. (YT total ",
$results->{'pageInfo'}->{'totalResults'} || 'N/A',
")"
);
$cb->(
{ items => \@items, offset => $wantedIndex, total => $total } );
}
};
_call( $method, $args, $pagingCb );
} }
sub _call { sub _call {
my ( $method, $args, $cb ) = @_; my ( $method, $args, $cb ) = @_;
my $API_KEY = $prefs->get('APIkey') || MIME::Base64::decode_base64(DEFAULT_API_KEY); my $API_KEY =
my $url = '?' . ($args->{_noKey} ? '' : 'key=' . $API_KEY . '&'); $prefs->get('APIkey') || MIME::Base64::decode_base64(DEFAULT_API_KEY);
my $url = '?' . ( $args->{_noKey} ? '' : 'key=' . $API_KEY . '&' );
$args->{regionCode} ||= $prefs->get('country') unless $args->{_noRegion}; $args->{regionCode} ||= $prefs->get('country') unless $args->{_noRegion};
$args->{part} ||= 'snippet' unless $args->{_noPart}; $args->{part} ||= 'snippet' unless $args->{_noPart};
$args->{maxResults} ||= 50; $args->{maxResults} ||= 50;
for my $k ( sort keys %{$args} ) { for my $k ( sort keys %{$args} ) {
next if $k =~ /^_/; next if $k =~ /^_/;
$url .= $k . '=' . URI::Escape::uri_escape_utf8( Encode::decode( 'utf8', $args->{$k} ) ) . '&'; $url .=
} $k . '='
. URI::Escape::uri_escape_utf8(
Encode::decode( 'utf8', $args->{$k} ) )
. '&';
}
$url =~ s/&$//; $url =~ s/&$//;
$url = API_URL . $method . $url; $url = API_URL . $method . $url;
my $cacheKey = $args->{_noCache} ? '' : md5_hex($url); my $cacheKey = $args->{_noCache} ? '' : md5_hex($url);
if ( $cacheKey && (my $cached = $cache->get($cacheKey)) ) { if ( $cacheKey && ( my $cached = $cache->get($cacheKey) ) ) {
main::INFOLOG && $log->info("Returning cached data for: $url"); main::INFOLOG && $log->info("Returning cached data for: $url");
$cb->($cached); $cb->($cached);
return; return;
} }
main::INFOLOG && $log->info("Calling API (will cache for ", $args->{_cache_ttl} || DEFAULT_CACHE_TTL, "s): $url"); main::INFOLOG && $log->info(
"Calling API (will cache for ",
$args->{_cache_ttl} || DEFAULT_CACHE_TTL,
"s): $url"
);
Slim::Networking::SimpleAsyncHTTP->new( Slim::Networking::SimpleAsyncHTTP->new(
sub { sub {
my $response = shift; my $response = shift;
my $result = eval { from_json($response->content) }; my $result = eval { from_json( $response->content ) };
if ($@) { if ($@) {
$log->error(Data::Dump::dump($response)) unless main::DEBUGLOG && $log->is_debug; $log->error( Data::Dump::dump($response) )
$log->error($@); unless main::DEBUGLOG && $log->is_debug;
} $log->error($@);
}
main::DEBUGLOG && $log->is_debug && warn Data::Dump::dump($result); main::DEBUGLOG && $log->is_debug && warn Data::Dump::dump($result);
$result ||= {}; $result ||= {};
$cache->set($cacheKey, $result, $args->{_cache_ttl} || DEFAULT_CACHE_TTL); $cache->set( $cacheKey, $result,
$args->{_cache_ttl} || DEFAULT_CACHE_TTL );
$cb->($result); $cb->($result);
}, },
sub { sub {
warn Data::Dump::dump(@_); warn Data::Dump::dump(@_);
$log->error($_[1]); $log->error( $_[1] );
$cb->( { error => $_[1] } ); $cb->( { error => $_[1] } );
}, },
{ {
timeout => 15, timeout => 15,
} }
)->get($url); )->get($url);
} }
1; 1;

View File

@@ -1,26 +1,26 @@
[% PROCESS settings/header.html %] [% PROCESS settings/header.html %]
<div>Hello World</div> <div>Hello World</div>
[% WRAPPER setting title="PLUGIN_YOUTUBEMUSIC_TESTPREF" desc="PLUGIN_YOUTUBEMUSIC_TESTPREF_DESC" %] [% WRAPPER setting title="PLUGIN_YOUTUBEMUSIC_TESTPREF" desc="PLUGIN_YOUTUBEMUSIC_TESTPREF_DESC" %]
<input type="text" class="stdedit" name="pref_testPref" id="pref_testPref" value="[% prefs.pref_testPref %]" size="45"> <input type="text" class="stdedit" name="pref_testPref" id="pref_testPref" value="[% prefs.pref_testPref %]" size="45">
[% END %]
[% WRAPPER setting title="PLUGIN_YOUTUBEMUSIC_AUTH" desc="PLUGIN_YOUTUBEMUSIC_AUTH_DESC" %]
<div>
[% IF !user_code %]
<input type="submit" name="get_device_code" value=[% "PLUGIN_YOUTUBEMUSIC_START_AUTH" | string %]>
[% ELSE %]
<a href="[% verification_url %]" target="none">[% "PLUGIN_YOUTUBEMUSIC_VERIFICATIONURL" | string %]</a>
<div>[% user_code %]</div>
[% END %] [% END %]
</div>
[% WRAPPER setting title="PLUGIN_YOUTUBEMUSIC_AUTH" desc="PLUGIN_YOUTUBEMUSIC_AUTH_DESC" %] <div>
<div> <input type="submit" name="get_token" value="Check">
[% IF !user_code %] [% IF access_token %]
<input type="submit" name="get_device_code" value=[% "PLUGIN_YOUTUBEMUSIC_START_AUTH" | string %]> <div>Success</div>
[% ELSE %]
<a href="[% verification_url %]" target="none">[% "PLUGIN_YOUTUBEMUSIC_VERIFICATIONURL" | string %]</a>
<div>[% user_code %]</div>
[% END %]
</div>
<div>
<input type="submit" name="get_token" value="Check">
[% IF access_token %]
<div>Success</div>
[% END %]
</div>
[% END %] [% END %]
</div>
[% END %]
[% PROCESS settings/footer.html %] [% PROCESS settings/footer.html %]

View File

@@ -8,93 +8,114 @@ use Slim::Utils::Strings qw(string cstring);
use JSON::XS::VersionOneAndTwo; use JSON::XS::VersionOneAndTwo;
use Data::Dumper; use Data::Dumper;
use constant CLIENT_ID => "861556708454-d6dlm3lh05idd8npek18k6be8ba3oc68.apps.googleusercontent.com"; use constant CLIENT_ID =>
"861556708454-d6dlm3lh05idd8npek18k6be8ba3oc68.apps.googleusercontent.com";
use constant CLIENT_SECRET => "SboVhoG9s0rNafixCSGGKXAT"; use constant CLIENT_SECRET => "SboVhoG9s0rNafixCSGGKXAT";
my $log = logger('plugin.youtubemusic'); my $log = logger('plugin.youtubemusic');
my $cache = Slim::Utils::Cache->new(); my $cache = Slim::Utils::Cache->new();
sub getDeviceCode { sub getDeviceCode {
my $post = "client_id=" . CLIENT_ID . "&scope=https://www.googleapis.com/auth/youtube"; my $post =
"client_id="
my $http = Slim::Networking::SimpleAsyncHTTP->new( . CLIENT_ID
sub { . "&scope=https://www.googleapis.com/auth/youtube";
my $response = shift;
my $result = eval { from_json($response->content) };
if ($@) { my $http = Slim::Networking::SimpleAsyncHTTP->new(
$log->error(Data::Dump::dump($response)) unless main::DEBUGLOG && $log->is_debug; sub {
$log->error($@); my $response = shift;
} else { my $result = eval { from_json( $response->content ) };
$cache->set("yt:device_code", $result->{device_code}, $result->{expires_in});
$cache->set("yt:verification_url", $result->{verification_url}, $result->{expires_in});
$cache->set("yt:user_code", $result->{user_code}, $result->{expires_in});
$log->debug("content:", $response->content); if ($@) {
} $log->error( Data::Dump::dump($response) )
}, unless main::DEBUGLOG && $log->is_debug;
sub { $log->error($@);
$log->error($_[1]); }
}, else {
{ $cache->set( "yt:device_code", $result->{device_code},
timeout => 15, $result->{expires_in} );
} $cache->set( "yt:verification_url", $result->{verification_url},
); $result->{expires_in} );
$cache->set( "yt:user_code", $result->{user_code},
$http->post( $result->{expires_in} );
"https://www.youtube.com/o/oauth2/device/code",
'Content-Type' => 'application/x-www-form-urlencoded', $log->debug( "content:", $response->content );
$post, }
); },
sub {
$log->error( $_[1] );
},
{
timeout => 15,
}
);
$http->post(
"https://www.youtube.com/o/oauth2/device/code",
'Content-Type' => 'application/x-www-form-urlencoded',
$post,
);
} }
sub getToken { sub getToken {
my $cb = shift; my $cb = shift;
my @params = @_; my @params = @_;
my $post = "client_id=" . CLIENT_ID . "&client_secret=" . CLIENT_SECRET; my $post = "client_id=" . CLIENT_ID . "&client_secret=" . CLIENT_SECRET;
my $code = $cache->get('yt:device_code'); my $code = $cache->get('yt:device_code');
if (defined $code) {
$post .= "&code=$code&grant_type=http://oauth.net/grant_type/device/1.0";
} else {
$post .= "&refresh_token=" . $cache->get('yt:refresh_token') . "&grant_type=refresh_token";
}
my $http = Slim::Networking::SimpleAsyncHTTP->new(
sub {
my $response = shift;
my $result = eval { from_json($response->content) };
if ($@) { if ( defined $code ) {
$log->error(Data::Dump::dump($response)) unless main::DEBUGLOG && $log->is_debug; $post .=
$log->error($@); "&code=$code&grant_type=http://oauth.net/grant_type/device/1.0";
} else { }
$cache->set("yt:access_token", $result->{access_token}, $result->{expires_in} - 60); else {
$cache->set('yt:refresh_token', $result->{refresh_token}) if $result->{refresh_token}; $post .=
"&refresh_token="
$cache->remove('yt:user_code'); . $cache->get('yt:refresh_token')
$cache->remove('yt:verification_url'); . "&grant_type=refresh_token";
$cache->remove('yt:device_code'); }
$log->debug("content:", $response->content);
$cb->(@params) if $cb; my $http = Slim::Networking::SimpleAsyncHTTP->new(
} sub {
}, my $response = shift;
sub { my $result = eval { from_json( $response->content ) };
$log->error($_[1]);
$cb->(@params) if $cb; if ($@) {
}, $log->error( Data::Dump::dump($response) )
{ unless main::DEBUGLOG && $log->is_debug;
timeout => 15, $log->error($@);
} }
); else {
$cache->set(
$http->post( "yt:access_token",
"https://oauth2.googleapis.com/token", $result->{access_token},
'Content-Type' => 'application/x-www-form-urlencoded', $result->{expires_in} - 60
$post, );
); $cache->set( 'yt:refresh_token', $result->{refresh_token} )
if $result->{refresh_token};
$cache->remove('yt:user_code');
$cache->remove('yt:verification_url');
$cache->remove('yt:device_code');
$log->debug( "content:", $response->content );
$cb->(@params) if $cb;
}
},
sub {
$log->error( $_[1] );
$cb->(@params) if $cb;
},
{
timeout => 15,
}
);
$http->post(
"https://oauth2.googleapis.com/token",
'Content-Type' => 'application/x-www-form-urlencoded',
$post,
);
} }
1; 1;

View File

@@ -11,34 +11,38 @@ use Slim::Utils::Cache;
use Plugins::YouTubeMusic::OAuth2; use Plugins::YouTubeMusic::OAuth2;
my $log = Slim::Utils::Log->addLogCategory({ my $log = Slim::Utils::Log->addLogCategory(
'category' => 'plugin.youtubemusic', {
'defaultLevel' => 'WARN', 'category' => 'plugin.youtubemusic',
'description' => 'PLUGIN_YOUTUBEMUSIC', 'defaultLevel' => 'WARN',
}); 'description' => 'PLUGIN_YOUTUBEMUSIC',
}
);
my $prefs = preferences('plugin.youtubemusic'); my $prefs = preferences('plugin.youtubemusic');
my $cache = Slim::Utils::Cache->new(); my $cache = Slim::Utils::Cache->new();
$prefs->init({ $prefs->init(
testPref => 'test' {
}); testPref => 'test'
}
);
sub initPlugin { sub initPlugin {
my $class = shift; my $class = shift;
$class->SUPER::initPlugin( $class->SUPER::initPlugin(
feed => \&mainMenu, feed => \&mainMenu,
tag => 'youtubemusic', tag => 'youtubemusic',
menu => 'radios', menu => 'radios',
is_app => 1, is_app => 1,
weight => 1, weight => 1,
); );
if (main::WEBUI) { if (main::WEBUI) {
require Plugins::YouTubeMusic::Settings; require Plugins::YouTubeMusic::Settings;
Plugins::YouTubeMusic::Settings->new(); Plugins::YouTubeMusic::Settings->new();
} }
} }
sub shutdownPlugin { sub shutdownPlugin {
@@ -48,59 +52,68 @@ sub shutdownPlugin {
sub getDisplayName { 'PLUGIN_YOUTUBEMUSIC' } sub getDisplayName { 'PLUGIN_YOUTUBEMUSIC' }
sub mainMenu { sub mainMenu {
my ($client, $callback, $args) = @_; my ( $client, $callback, $args ) = @_;
$callback->([ $callback->(
{ name => cstring($client, 'PLUGIN_YOUTUBEMUSIC_MYPLAYLISTS'), type => 'url', url => \&myPlaylistHandler, passthrough => [ { count => 2 } ] }, [
]); {
name => cstring( $client, 'PLUGIN_YOUTUBEMUSIC_MYPLAYLISTS' ),
type => 'url',
url => \&myPlaylistHandler,
passthrough => [ { count => 2 } ]
},
]
);
} }
sub myPlaylistHandler { sub myPlaylistHandler {
my ($client, $cb, $args, $params) = @_; my ( $client, $cb, $args, $params ) = @_;
if (!$cache->get('yt:access_token')) { if ( !$cache->get('yt:access_token') ) {
Plugins::YouTubeMusic::OAuth2::getToken(\&myPlaylistHandler, @_); Plugins::YouTubeMusic::OAuth2::getToken( \&myPlaylistHandler, @_ );
return; return;
} }
my $account = { my $account = {
_cache_ttl => 60, _cache_ttl => 60,
_noKey => 1, _noKey => 1,
mine => 'true', mine => 'true',
access_token => $cache->get('yt:access_token'), access_token => $cache->get('yt:access_token'),
}; };
Plugins::YouTubeMusic::API->searchDirect('playlists', sub { Plugins::YouTubeMusic::API->searchDirect(
$cb->(_renderList($_[0], 'title', $account)); 'playlists',
}, { sub {
%$account, $cb->( _renderList( $_[0], 'title', $account ) );
_index => $args->{index}, },
_quantity => $args->{quantity}, {
}); %$account,
_index => $args->{index},
_quantity => $args->{quantity},
}
);
} }
sub _renderList { sub _renderList {
my ($list, $sort, $passthrough, $tags) = @_; my ( $list, $sort, $passthrough, $tags ) = @_;
my $sortedList = $list->{items}; my $sortedList = $list->{items};
my @items; my @items;
for my $entry (@$sortedList) { for my $entry (@$sortedList) {
my $snippet = $entry->{snippet} || next; my $snippet = $entry->{snippet} || next;
my $title = $snippet->{title} || next; my $title = $snippet->{title} || next;
my $item = { my $item = {
name => $title, name => $title,
type => 'playlist', type => 'playlist',
}; };
push @items, $item; push @items, $item;
} }
$list->{items} = \@items; $list->{items} = \@items;
return $list; return $list;
} }
1; 1;

View File

@@ -6,37 +6,37 @@ use base qw(Slim::Web::Settings);
use Slim::Utils::Log; use Slim::Utils::Log;
use Slim::Utils::Prefs; use Slim::Utils::Prefs;
my $log = logger('plugin.youtubemusic'); my $log = logger('plugin.youtubemusic');
my $cache = Slim::Utils::Cache->new(); my $cache = Slim::Utils::Cache->new();
sub name { sub name {
return 'PLUGIN_YOUTUBEMUSIC'; return 'PLUGIN_YOUTUBEMUSIC';
} }
sub page { sub page {
return 'plugins/YouTubeMusic/settings/basic.html'; return 'plugins/YouTubeMusic/settings/basic.html';
} }
sub prefs { sub prefs {
return (preferences('plugin.youtubemusic'), qw(testPref)); return ( preferences('plugin.youtubemusic'), qw(testPref) );
} }
sub handler { sub handler {
my ($class, $client, $params) = @_; my ( $class, $client, $params ) = @_;
if ($params->{'saveSettings'}) { if ( $params->{'saveSettings'} ) {
preferences('plugin.youtubemusic')->set('testPref', $params->{'pref_testPref'}); preferences('plugin.youtubemusic')
} ->set( 'testPref', $params->{'pref_testPref'} );
}
Plugins::YouTubeMusic::OAuth2::getDeviceCode if $params->{get_device_code}; Plugins::YouTubeMusic::OAuth2::getDeviceCode if $params->{get_device_code};
Plugins::YouTubeMusic::OAuth2::getToken if $params->{get_token}; Plugins::YouTubeMusic::OAuth2::getToken if $params->{get_token};
$params->{user_code} = $cache->get('yt:user_code'); $params->{user_code} = $cache->get('yt:user_code');
$params->{verification_url} = $cache->get('yt:verification_url'); $params->{verification_url} = $cache->get('yt:verification_url');
$params->{access_token} = $cache->get('yt:access_token'); $params->{access_token} = $cache->get('yt:access_token');
return $class->SUPER::handler($client, $params); return $class->SUPER::handler( $client, $params );
} }
1; 1;