Peertube-dl/lib/Peertube/DL/Downloaders.pm
2021-01-18 02:28:53 +01:00

305 lines
11 KiB
Perl

package Peertube::DL::Downloaders;
use strict;
use warnings;
use feature 'say';
use Symbol 'gensym';
use IPC::Open3;
use File::Basename;
use JSON;
use Data::Dumper;
use Mojo::DOM;
use Peertube::DL::Javascript;
use Peertube::DL::Utils;
sub youtube {
my $ua = shift;
my $response = shift;
my $options = shift;
my $dom = Mojo::DOM->new( $response->decoded_content );
my $script_tag = $dom->find('script')->grep(
sub {
$_[0]->text =~ /var ytInitialPlayerResponse =/;
}
)->first;
my ($ytInitialPlayerResponse) =
$script_tag->text =~ /^var ytInitialPlayerResponse = (.*?\});var meta/;
$ytInitialPlayerResponse = JSON::from_json($ytInitialPlayerResponse);
my $microformat =
$ytInitialPlayerResponse->{microformat}{playerMicroformatRenderer};
$ytInitialPlayerResponse = $ytInitialPlayerResponse->{streamingData};
if ( defined $options->{format} ) {
my $format = $options->{format};
($format) = grep { $_->{itag} eq $format } (
scalar @{ $ytInitialPlayerResponse->{adaptiveFormats} }
? @{ $ytInitialPlayerResponse->{adaptiveFormats} }
: (),
scalar @{ $ytInitialPlayerResponse->{formats} }
? @{ $ytInitialPlayerResponse->{formats} }
: ()
);
my $mime_type = $format->{mimeType} =~ s/;.*$//r;
my $extension = $mime_type =~ s/^.*?\///r =~ s/;.*$//r;
if ( defined $format->{url} ) {
return {
filename => $microformat->{title}{simpleText} . '.'
. $extension,
url => $format->{url},
};
}
my $url_data = $format->{signatureCipher};
$url_data = {
map {
my ( $a, $b ) = /(.*?)=(.*)$/;
(
$a => Peertube::DL::Utils::uri_decode(
Peertube::DL::Utils::uri_decode($b)
)
)
} split '&',
$url_data
};
my ($player_url) =
$response->decoded_content =~ m/"jsUrl"\s*:\s*("[^"]+")/;
$player_url = JSON::from_json( $player_url, { allow_nonref => 1 } );
$player_url = 'https://www.youtube.com' . $player_url
unless $player_url =~ m'^https://www.youtube.com';
say $player_url;
my $hostname = $player_url =~ s/https?:\/\///r;
$hostname = $hostname =~ s/\/.*$//;
$hostname = JSON::encode_json($hostname);
my $js = Peertube::DL::Javascript->new;
my $player_response = $ua->get($player_url);
my $regexes_search_function_name = [
qr/\b[cs]\s*&&\s*[adf]\.set\([^,]+\s*,\s*encodeURIComponent\s*\(\s*(?<sig>(?:[a-zA-Z0-9]|\$)+)\(/,
qr/\b[a-zA-Z0-9]+\s*&&\s*[a-zA-Z0-9]+\.set\([^,]+\s*,\s*encodeURIComponent\s*\(\s*(?<sig>(?:[a-zA-Z0-9]|\$)+)\(/,
qr/(?:\b|(?:[^a-zA-Z0-9]|\$))(?<sig>(?:[a-zA-Z0-9]|\$){2})\s*=\s*function\(\s*a\s*\)\s*{\s*a\s*=\s*a\.split\(\s*""\s*\)/,
qr/(?<sig>(?:[a-zA-Z0-9]|\$)+)\s*=\s*function\(\s*a\s*\)\s*{\s*a\s*=\s*a\.split\(\s*""\s*\)/,
];
my $function_name_regen_sig;
for my $regex (@$regexes_search_function_name) {
($function_name_regen_sig) =
$player_response->decoded_content =~ /$regex/;
last if defined $function_name_regen_sig;
}
my ($extract_function_sig) = $player_response->decoded_content =~
m/\n(${function_name_regen_sig}=function\([a-zA-Z]+\)\{.*?\};)\n/;
my ($class_decoding) =
$extract_function_sig =~ /;([a-zA-Z]+)\.[a-zA-Z]+\(/;
my ($class_decoding_code) = $player_response->decoded_content =~
/;(var ${class_decoding}=\{.*?\}\};)/s;
$js->evalJS( join "\n", $class_decoding_code );
$js->evalJS( join "\n", $extract_function_sig );
my $signature =
$js->callJSFunction( $function_name_regen_sig, $url_data->{s} );
my $url = $url_data->{url} . "&sig=${signature}";
say $url;
return {
url => $url,
filename => $microformat->{title}{simpleText} . '.' . $extension,
};
} else {
my @formats = map {
{
id => $_->{itag},
mimeType => $_->{mimeType},
(
( defined $_->{averageBitrate} && defined $_->{bitrate} )
? ( bitrate => $_->{averageBitrate} // $_->{bitrate} )
: ()
),
(
( defined $_->{qualityLabel} )
? ( qualityLabel => $_->{qualityLabel} =~ s/p.*$//r )
: ()
),
(
( defined $_->{audioSampleRate} )
? ( audioSampleRate => $_->{audioSampleRate} )
: ()
),
(
( defined $_->{quality} )
? ( quality => $_->{quality} )
: ()
),
}
} (
scalar @{ $ytInitialPlayerResponse->{adaptiveFormats} }
? @{ $ytInitialPlayerResponse->{adaptiveFormats} }
: (),
scalar @{ $ytInitialPlayerResponse->{formats} }
? @{ $ytInitialPlayerResponse->{formats} }
: ()
);
my $video_formats = [
sort {
$b->{qualityLabel} <=> $a->{qualityLabel}
|| ( $b->{bitrate} // 0 ) <=> ( $a->{bitrate} // 0 )
} grep { defined $_->{qualityLabel} } @formats
];
my $audio_formats = [
sort {
$a->{audioSampleRate} <=> $b->{audioSampleRate}
|| $b->{bitrate} <=> $a->{bitrate}
} grep {
defined $_->{audioSampleRate}
&& $_->{mimeType} =~ /webm;/
} @formats
];
return {
options => { list_formats => 1 },
title => $microformat->{title}{simpleText},
description => $microformat->{description}{simpleText},
formats => {
video_formats => $video_formats,
audio_formats => $audio_formats,
},
};
}
}
sub kjanime {
my $ua = shift;
my $response = shift;
my $dom = Mojo::DOM->new( $response->decoded_content );
my $hotlink_span = $dom->find('.spoikj .ddserver')->grep(
sub {
my $i = shift;
$i->text =~ /HotLink/;
}
)->first;
my ($k_poi) = $hotlink_span->attr('onclick') =~ /getKpoi\((.*)\)/;
$k_poi =~ s/'//g;
my ( $t, $s, $l ) = ( split /,/, $k_poi );
my $url_get_php =
'https://kjanime.net/link/get.php?t='
. Peertube::DL::Utils::uri_encode($t) . '&s='
. Peertube::DL::Utils::uri_encode($s) . '&l='
. Peertube::DL::Utils::uri_encode($l);
my $get_php_response = $ua->get(
$url_get_php,
'X-Requested-With' => 'XMLHttpRequest',
);
$dom = Mojo::DOM->new( $get_php_response->decoded_content );
my $links = [ map { $_->attr('href') =~ s/^http:\/\//https:\/\//r }
@{ $dom->find('a')->to_array } ];
return { options => { list => 1 }, urls => $links };
}
sub kjanime_ch {
my $ua = shift;
my $response = shift;
my ($id) = $response->filename;
say $id;
my $param = join '',
map { Peertube::DL::Utils::uri_decode("%$_") } unpack( "(A2)*", $id );
say $param;
# FORMAT: KJA://70714B6E314943383135712B72582B70713768797A4B6E4D734D474775773D3D/1
# REQUEST: GET https://hotlink-api.kjanime.net/dl/serie.php?id=70714B6E314943383135712B72582B70713768797A4B6E4D734D474775773D3D&ep=1
$param =~ s/^KJA:\/\///;
my ( $real_id, $ep ) = split '/', $param;
my $url_serie_php =
'https://hotlink-api.kjanime.net/dl/serie.php?id='
. Peertube::DL::Utils::uri_encode($real_id) . '&ep='
. Peertube::DL::Utils::uri_encode($ep);
say $url_serie_php;
my $response_serie_php = $ua->get(
$url_serie_php,
'X-Requested-With' => 'XMLHttpRequest',
);
my $download_data =
JSON::decode_json( $response_serie_php->decoded_content );
say Data::Dumper::Dumper $download_data;
$download_data->{filename} = delete $download_data->{name};
$download_data->{url} = delete $download_data->{link};
return $download_data;
}
sub gocdn {
my $ua = shift;
my $response = shift;
my ($id) = $response->decoded_content =~ /gocdn\.html#(.+?)"/;
die "Id undefined" if !defined $id;
my $url_gocdn = 'https://streamium.xyz/gocdn.php?v=' . $id;
my $response_gocdn = $ua->get($url_gocdn);
die $response_gocdn->status_line . ' from ' . $url_gocdn
unless $response_gocdn->is_success;
my $google_url =
JSON::decode_json( $response_gocdn->decoded_content )->{file};
my ($filename) = $response->base =~ s/^.*\///gr . '.mp4';
return { url => $google_url, filename => $filename };
}
sub animeid {
my $ua = shift;
my $response = shift;
my @order = ( 'id', 'title', 'refer' );
my $url = $response->base;
my $url_ajax = 'https://animeid.to/ajax.php?';
my ($get_params) =
$response->decoded_content =~ /animeid\.to\/streaming\.php\?(.*?)"/;
$get_params = {
map {
my ( $key, $value ) = /^(.*?)=(.*)$/;
( $key, Peertube::DL::Utils::uri_decode($value) )
} split '&',
$get_params
};
say "Got from $url params for ajax:";
print Data::Dumper::Dumper $get_params;
say 'Adding passed url as refer.';
$get_params->{refer} = $url;
my $filename = $get_params->{title};
$url_ajax .= join '&',
map {
join '=',
( $_ => Peertube::DL::Utils::uri_encode( $get_params->{$_} ) )
} @order;
say 'Getting video location from: ' . $url_ajax;
my $ajax_data = $ua->get(
$url_ajax,
Referer => $url,
Host => 'animeid.to',
'X-Requested-With' => 'XMLHttpRequest',
)->decoded_content;
say "Decoding json... $ajax_data";
$ajax_data = JSON::decode_json $ajax_data;
die 'No video source found.' if ( !defined $ajax_data->{source} );
my $download_redirect_url = $ajax_data->{source}[0]{file}
// die "No url found.";
my $extension = $ajax_data->{source}[0]{type} // die "No extension found.";
$filename .= ".$extension";
say "Getting redirect to download url from $download_redirect_url...";
my $video_response = $ua->get(
$download_redirect_url,
Referer => $url,
Host => 'animeid.to',
);
if ( $video_response->is_redirect ) {
my $video_location = $video_response->header('Location');
die "No redirection." unless $video_location;
return {
url => $video_location,
filename => $filename,
};
} else {
die 'Getting redirect failed because: ' . $video_response->status_line;
}
}
1;