@@ -83,6 +83,8 @@ my %valid_options = (
8383 escape_utf8 => {valid => [1, 0], default => 0},
8484 prefer_mp4 => {valid => [1, 0], default => 0},
8585
86+ use_invidious_api => {valid => [1, 0], default => 0},
87+
8688 # OAuth stuff
8789 client_id => {valid => [qr / ^.{5}/ ], default => undef },
8890 client_secret => {valid => [qr / ^.{5}/ ], default => undef },
@@ -251,15 +253,15 @@ sub set_lwp_useragent {
251253 or $response -> request-> method ne ' GET' # cache only GET requests
252254
253255 # don't cache if "cache-control" specifies "max-age=0" or "no-store"
254- or $response -> header(' cache-control' ) =~ / \b (?:max-age=0|no-store)\b /
256+ or (( $response -> header(' cache-control' ) // ' ' ) =~ / \b (?:max-age=0|no-store)\b / )
255257
256258 # don't cache video or audio files
257- or $response -> header(' content-type' ) =~ / \b (?:video|audio)\b / ;
259+ or (( $response -> header(' content-type' ) // ' ' ) =~ / \b (?:video|audio)\b / ) ;
258260 },
259261
260262 recache_if => sub {
261263 my ($response , $path ) = @_ ;
262- not ($response -> is_fresh) # recache if the response expired
264+ not ($response -> is_fresh) # recache if the response expired
263265 or ($response -> code == 404 && -M $path > 1); # recache any 404 response older than 1 day
264266 }
265267 )
@@ -269,10 +271,10 @@ sub set_lwp_useragent {
269271 );
270272
271273 require LWP::ConnCache;
272- my $cache = LWP::ConnCache-> new;
274+ state $cache = LWP::ConnCache-> new;
273275 $cache -> total_capacity(undef ); # no limit
274276
275- my $accepted_encodings = do {
277+ state $accepted_encodings = do {
276278 require HTTP::Message;
277279 HTTP::Message::decodable();
278280 };
@@ -309,7 +311,7 @@ sub prepare_access_token {
309311 return ;
310312}
311313
312- sub _get_lwp_header {
314+ sub _auth_lwp_header {
313315 my ($self ) = @_ ;
314316
315317 my %lwp_header ;
@@ -344,7 +346,7 @@ sub lwp_get {
344346 $url // return ;
345347 $self -> {lwp } // $self -> set_lwp_useragent();
346348
347- my %lwp_header = ($opt {simple } ? () : $self -> _get_lwp_header );
349+ my %lwp_header = ($opt {simple } ? () : $self -> _auth_lwp_header );
348350 my $response = $self -> {lwp }-> get($url , %lwp_header );
349351
350352 if ($response -> is_success) {
@@ -358,7 +360,7 @@ sub lwp_get {
358360 $self -> set_access_token($refresh_token -> {access_token });
359361
360362 # Don't be tempted to use recursion here, because bad things will happen!
361- $response = $self -> {lwp }-> get($url , $self -> _get_lwp_header );
363+ $response = $self -> {lwp }-> get($url , $self -> _auth_lwp_header );
362364
363365 if ($response -> is_success) {
364366 $self -> save_authentication_tokens();
@@ -490,38 +492,89 @@ sub _make_feed_url {
490492 $self -> get_feeds_url() . $path . ' ?' . $self -> default_arguments(%args );
491493}
492494
493- sub _get_formats_from_ytdl {
495+ sub _extract_from_indivious {
494496 my ($self , $videoID ) = @_ ;
495497
498+ my $url = sprintf (" https://invidio.us/api/v1/videos/%s ?fields=formatStreams,adaptiveFormats" , $videoID );
499+
500+ (my $resp = $self -> {lwp }-> get($url ))-> is_success() || return ;
501+ my $json = $resp -> decoded_content() // return ;
502+ my $ref = $self -> parse_json_string($json ) // return ;
503+
504+ my @formats ;
505+
506+ # The entries are already in the format that we want.
507+ if (exists ($ref -> {adaptiveFormats }) and ref ($ref -> {adaptiveFormats }) eq ' ARRAY' ) {
508+ push @formats , @{$ref -> {adaptiveFormats }};
509+ }
510+
511+ if (exists ($ref -> {formatStreams }) and ref ($ref -> {formatStreams }) eq ' ARRAY' ) {
512+ push @formats , @{$ref -> {formatStreams }};
513+ }
514+
515+ return @formats ;
516+ }
517+
518+ sub _fallback_extract_urls {
519+ my ($self , $videoID ) = @_ ;
520+
521+ my @array ;
522+
523+ if ($self -> get_use_invidious_api) { # use the API of invidio.us
524+
525+ if ($self -> get_debug) {
526+ say STDERR " :: Using the API of invidio.us..." ;
527+ }
528+
529+ push @array , $self -> _extract_from_indivious($videoID );
530+
531+ if ($self -> get_debug) {
532+ say STDERR " :: Found " , scalar (@array ), " streaming URLs." ;
533+ }
534+
535+ if (@array ) {
536+ return @array ;
537+ }
538+ }
539+
496540 ((state $x = $self -> proxy_system(' youtube-dl' , ' --version' )) == 0)
497541 || return ;
498542
543+ if ($self -> get_debug) {
544+ say STDERR " :: Using youtube-dl to extract streaming URLs..." ;
545+ }
546+
499547 my $json = $self -> proxy_stdout(' youtube-dl' , ' --all-formats' , ' --dump-single-json' ,
500548 quotemeta (" https://www.youtube.com/watch?v=" . $videoID ));
501549
502- my @array ;
503550 my $ref = $self -> parse_json_string($json ) // return ;
551+
504552 if (ref ($ref ) eq ' HASH' and exists ($ref -> {formats }) and ref ($ref -> {formats }) eq ' ARRAY' ) {
505553 foreach my $format (@{$ref -> {formats }}) {
506554 if (exists ($format -> {format_id }) and exists ($format -> {url })) {
507555
508- push @array ,
509- {
510- itag => $format -> {format_id },
511- url => $format -> {url },
512- type => (
513- (
514- (defined ($format -> {format_note }) && $format -> {format_note } eq ' DASH audio' )
515- ? ' audio/'
516- : ' video/'
517- )
518- . $format -> {ext }
519- ),
520- };
556+ my $entry = {
557+ itag => $format -> {format_id },
558+ url => $format -> {url },
559+ type => (
560+ (
561+ (defined ($format -> {format_note }) and $format -> {format_note } eq ' DASH audio' )
562+ ? ' audio/'
563+ : ' video/'
564+ )
565+ . $format -> {ext }
566+ ),
567+ };
568+
569+ push @array , $entry ;
521570 }
522571 }
523572 }
524573
574+ if ($self -> get_debug) {
575+ say STDERR " :: Found " , scalar (@array ), " streaming URLs." ;
576+ }
577+
525578 return @array ;
526579}
527580
@@ -601,7 +654,7 @@ sub _extract_streaming_urls {
601654 foreach my $video (@results ) {
602655 if (exists $video -> {s }) { # has an encrypted signature :(
603656
604- my @formats = $self -> _get_formats_from_ytdl ($videoID );
657+ my @formats = $self -> _fallback_extract_urls ($videoID );
605658
606659 foreach my $format (@formats ) {
607660 foreach my $ref (@results ) {
@@ -617,7 +670,7 @@ sub _extract_streaming_urls {
617670 }
618671
619672 if ($info -> {livestream } or $info -> {live_playback }) {
620- if (my @formats = $self -> _get_formats_from_ytdl ($videoID )) {
673+ if (my @formats = $self -> _fallback_extract_urls ($videoID )) {
621674 @results = @formats ;
622675 }
623676 elsif (exists $info -> {hlsvp }) {
@@ -643,7 +696,7 @@ Returns a list of streaming URLs for a videoID.
643696sub get_streaming_urls {
644697 my ($self , $videoID ) = @_ ;
645698
646- my $url = ($self -> get_video_info_url() . sprintf ($self -> get_video_info_args(), $videoID ));
699+ my $url = ($self -> get_video_info_url() . sprintf ($self -> get_video_info_args(), $videoID ));
647700 my $content = $self -> lwp_get($url , simple => 1) // return ;
648701 my %info = $self -> parse_query_string($content );
649702
@@ -670,7 +723,7 @@ sub get_streaming_urls {
670723
671724 # Try again with youtube-dl
672725 if (!@streaming_urls or $info {status } =~ / fail|error/i ) {
673- @streaming_urls = $self -> _get_formats_from_ytdl ($videoID );
726+ @streaming_urls = $self -> _fallback_extract_urls ($videoID );
674727 }
675728
676729 if ($self -> get_prefer_mp4) {
0 commit comments