forked from sergiotarxz/Peertube-dl
Merge pull request 'feature/adding_youtube_support_and_a_js_interpreter' (#1) from feature/adding_youtube_support_and_a_js_interpreter into master
Reviewed-on: https://gitea.sergiotarxz.freemyip.com/sergiotarxz/Peertube-dl/pulls/1
This commit is contained in:
commit
bc52ffc855
10
MANIFEST
10
MANIFEST
@ -1,14 +1,17 @@
|
|||||||
.proverc
|
.proverc
|
||||||
AUTHORS
|
AUTHORS
|
||||||
|
bin/js-pruebas
|
||||||
bin/peertube-dl
|
bin/peertube-dl
|
||||||
bin/peertube-dl-hypnotoad
|
bin/peertube-dl-hypnotoad
|
||||||
bin/peertube-dl-web
|
bin/peertube-dl-web
|
||||||
bin/peertube-dl-web.conf
|
bin/peertube-dl-web.conf
|
||||||
cpanfile
|
cpanfile
|
||||||
|
javascript_interpreter_xs/javascript.xs
|
||||||
|
javascript_interpreter_xs/Makefile.PL
|
||||||
lib/Peertube/DL.pm
|
lib/Peertube/DL.pm
|
||||||
lib/Peertube/DL/Downloaders.pm
|
lib/Peertube/DL/Downloaders.pm
|
||||||
|
lib/Peertube/DL/Javascript.pm
|
||||||
lib/Peertube/DL/public/css/index.css
|
lib/Peertube/DL/public/css/index.css
|
||||||
lib/Peertube/DL/public/css/spinner.css
|
|
||||||
lib/Peertube/DL/public/img/spinner.svg
|
lib/Peertube/DL/public/img/spinner.svg
|
||||||
lib/Peertube/DL/public/index.html
|
lib/Peertube/DL/public/index.html
|
||||||
lib/Peertube/DL/public/js/peertube-dl-web.js
|
lib/Peertube/DL/public/js/peertube-dl-web.js
|
||||||
@ -17,8 +20,11 @@ lib/Peertube/DL/UserAgent.pm
|
|||||||
lib/Peertube/DL/Utils.pm
|
lib/Peertube/DL/Utils.pm
|
||||||
LICENSE
|
LICENSE
|
||||||
Makefile.PL
|
Makefile.PL
|
||||||
MANIFEST This list of files
|
MANIFEST
|
||||||
README.md
|
README.md
|
||||||
|
src/include/javascript_builtins.h
|
||||||
|
src/javascript_builtins.c
|
||||||
|
src/Makefile.PL
|
||||||
t/00-use_ok.t
|
t/00-use_ok.t
|
||||||
t/downloaders/animeflv_example_response.html
|
t/downloaders/animeflv_example_response.html
|
||||||
t/downloaders/gocdn.t
|
t/downloaders/gocdn.t
|
||||||
|
12
Makefile.PL
12
Makefile.PL
@ -4,6 +4,18 @@ WriteMakefile(
|
|||||||
NAME => 'Peertube::DL',
|
NAME => 'Peertube::DL',
|
||||||
VERSION => '0.1',
|
VERSION => '0.1',
|
||||||
INST_SCRIPT => './bin',
|
INST_SCRIPT => './bin',
|
||||||
|
INST_BIN => './bin',
|
||||||
test => { TESTS => 't/*.t' },
|
test => { TESTS => 't/*.t' },
|
||||||
test => { TESTS => 't/*/*.t' },
|
test => { TESTS => 't/*/*.t' },
|
||||||
|
DIR => [ 'src', 'javascript_interpreter_xs/' ],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
sub MY::postamble {
|
||||||
|
'
|
||||||
|
src: src/Makefile
|
||||||
|
src && $(MAKE) $(PASSTHRU)
|
||||||
|
javascript_interpreter_xs: javascript_interpreter_xs/Makefile
|
||||||
|
cd javascript_interpreter_xs/ && $(MAKE) $(PASSTHRU)
|
||||||
|
';
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@ animeflv.cc
|
|||||||
|
|
||||||
kjanime.net
|
kjanime.net
|
||||||
|
|
||||||
|
youtube.com (Video only in webpage, all formats on cli.)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
There is two alternative ways applying to the GNU/Linux OS:
|
There is two alternative ways applying to the GNU/Linux OS:
|
||||||
|
16
bin/js-pruebas
Executable file
16
bin/js-pruebas
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
#
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use feature 'say';
|
||||||
|
|
||||||
|
use Peertube::DL::Javascript;
|
||||||
|
|
||||||
|
my $js = Peertube::DL::Javascript->new;
|
||||||
|
eval {
|
||||||
|
say $js->evalJS("if(\"undefined\"!=typeof Reflect&&Reflect.construct){if(a())return Reflect.construct;var b=Reflect.construct;return function(c,d,e){c=b(c,d);e&&Reflect.setPrototypeOf(c,e.prototype);return c}}return function(c,d,e){void 0===e&&(e=c);");
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
warn $@;
|
||||||
|
}
|
@ -9,9 +9,12 @@ use Getopt::Long::Descriptive;
|
|||||||
|
|
||||||
use Peertube::DL::URLHandler;
|
use Peertube::DL::URLHandler;
|
||||||
|
|
||||||
|
binmode STDOUT, ':utf8';
|
||||||
|
|
||||||
my ( $opt, $usage ) = describe_options(
|
my ( $opt, $usage ) = describe_options(
|
||||||
'peertube-dl %o <url>',
|
'peertube-dl %o <url>',
|
||||||
[ 'recurse|r', 'Recursive in reproduction lists.', { default => 0 } ],
|
[ 'recurse|r', 'Recursive in reproduction lists.', { default => 0 } ],
|
||||||
|
[ 'format|f=s', 'Choose format by id.', ],
|
||||||
[],
|
[],
|
||||||
[ 'help|h', 'Show this help.', { shortcircuit => 1 } ],
|
[ 'help|h', 'Show this help.', { shortcircuit => 1 } ],
|
||||||
);
|
);
|
||||||
@ -19,10 +22,12 @@ my ( $opt, $usage ) = describe_options(
|
|||||||
print( $usage->text ), exit if $opt->help;
|
print( $usage->text ), exit if $opt->help;
|
||||||
|
|
||||||
my $recurse = $opt->recurse;
|
my $recurse = $opt->recurse;
|
||||||
|
my $format = $opt->format;
|
||||||
|
|
||||||
die "No url passed" unless @ARGV;
|
die "No url passed" unless @ARGV;
|
||||||
|
|
||||||
my $download_data = Peertube::DL::URLHandler::getDownloadDataFromURL( $ARGV[0] );
|
my $download_data =
|
||||||
|
Peertube::DL::URLHandler::getDownloadDataFromURL( $ARGV[0], { defined $format ? ( format => $format ) : () } );
|
||||||
|
|
||||||
my $ua = Peertube::DL::URLHandler::generateUA();
|
my $ua = Peertube::DL::URLHandler::generateUA();
|
||||||
|
|
||||||
@ -31,13 +36,14 @@ if ( defined $download_data->{options}{list} && $download_data->{options}{list}
|
|||||||
if ($recurse) {
|
if ($recurse) {
|
||||||
for my $url (@$urls) {
|
for my $url (@$urls) {
|
||||||
say "Handling nested url: $url";
|
say "Handling nested url: $url";
|
||||||
my $url_download_data = Peertube::DL::URLHandler::getDownloadDataFromURL( $url );
|
my $url_download_data = Peertube::DL::URLHandler::getDownloadDataFromURL($url);
|
||||||
downloadVideo( $ua, $url_download_data );
|
downloadVideo( $ua, $url_download_data );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
say "The urls are:\n" . join "\n", @$urls;
|
say "The urls are:\n" . join "\n", @$urls;
|
||||||
}
|
}
|
||||||
|
} elsif ( defined $download_data->{options}{list_formats} && $download_data->{options}{list_formats} ) {
|
||||||
|
exit 0;
|
||||||
} else {
|
} else {
|
||||||
downloadVideo( $ua, $download_data );
|
downloadVideo( $ua, $download_data );
|
||||||
}
|
}
|
||||||
@ -52,7 +58,7 @@ sub downloadVideo {
|
|||||||
die "Cannot retrieve video data" unless $response->is_success;
|
die "Cannot retrieve video data" unless $response->is_success;
|
||||||
|
|
||||||
my $content = $response->decoded_content;
|
my $content = $response->decoded_content;
|
||||||
|
|
||||||
say "Writing into $filename.";
|
say "Writing into $filename.";
|
||||||
open my $fh, '>', $filename or die "Cannot open $filename";
|
open my $fh, '>', $filename or die "Cannot open $filename";
|
||||||
$fh->print( $response->decoded_content ) or die "Cannot write to $filename";
|
$fh->print( $response->decoded_content ) or die "Cannot write to $filename";
|
||||||
|
@ -16,10 +16,20 @@ get '/' => sub {
|
|||||||
|
|
||||||
post '/api' => sub {
|
post '/api' => sub {
|
||||||
my $c = shift;
|
my $c = shift;
|
||||||
my $url = $c->req->json->{url}
|
my $url = $c->req->json->{url} // (
|
||||||
// ( return $c->render( text => 'Malformed request due missing url json parameter.', status => 400 ) );
|
return $c->render(
|
||||||
|
text => 'Malformed request due missing url json parameter.',
|
||||||
|
status => 400
|
||||||
|
)
|
||||||
|
);
|
||||||
my $render_returned;
|
my $render_returned;
|
||||||
eval { $render_returned = $c->render( json => Peertube::DL::URLHandler::getDownloadDataFromURL($url) ); };
|
eval {
|
||||||
|
$render_returned = $c->render(
|
||||||
|
json => Peertube::DL::URLHandler::getDownloadDataFromURL(
|
||||||
|
$url, { format => 18 }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
return $c->render( text => $@, status => 500 );
|
return $c->render( text => $@, status => 500 );
|
||||||
}
|
}
|
||||||
@ -28,9 +38,15 @@ post '/api' => sub {
|
|||||||
|
|
||||||
post '/proxy_to_get' => sub {
|
post '/proxy_to_get' => sub {
|
||||||
my $c = shift;
|
my $c = shift;
|
||||||
my $url = $c->req->json->{url}
|
my $url = $c->req->json->{url} // (
|
||||||
// ( return $c->render( text => 'Malformed request due missing url json parameter.', status => 400 ) );
|
return $c->render(
|
||||||
die "Not supported url" unless $url =~ s/^https:\/\///r =~ /^storage\.googleapis\.com/;
|
text => 'Malformed request due missing url json parameter.',
|
||||||
|
status => 400
|
||||||
|
)
|
||||||
|
);
|
||||||
|
die "Not supported url"
|
||||||
|
unless ( $url =~ s/^https?:\/\///r =~ /^storage\.googleapis\.com/
|
||||||
|
|| $url =~ s/https?:\/\///r =~ s/\/.*$//r =~ /googlevideo\.com$/ );
|
||||||
my $ua = Peertube::DL::URLHandler::generateUA();
|
my $ua = Peertube::DL::URLHandler::generateUA();
|
||||||
return $c->render(
|
return $c->render(
|
||||||
data => $ua->get($url)->decoded_content,
|
data => $ua->get($url)->decoded_content,
|
||||||
@ -38,8 +54,10 @@ post '/proxy_to_get' => sub {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
push @{ app->renderer->paths }, ( Peertube::DL::getDir() . '/Peertube/DL/templates' );
|
push @{ app->renderer->paths },
|
||||||
push @{ app->static->paths }, ( Peertube::DL::getDir() . '/Peertube/DL/public' );
|
( Peertube::DL::getDir() . '/Peertube/DL/templates' );
|
||||||
|
push @{ app->static->paths },
|
||||||
|
( Peertube::DL::getDir() . '/Peertube/DL/public' );
|
||||||
|
|
||||||
my $config = plugin 'Config';
|
my $config = plugin 'Config';
|
||||||
|
|
||||||
|
6
cpanfile
6
cpanfile
@ -7,3 +7,9 @@ requires 'Test::Most';
|
|||||||
requires 'Test::MockObject';
|
requires 'Test::MockObject';
|
||||||
requires 'Mojo::Server::Hypnotoad';
|
requires 'Mojo::Server::Hypnotoad';
|
||||||
requires 'Getopt::Long::Descriptive';
|
requires 'Getopt::Long::Descriptive';
|
||||||
|
requires 'Perl::Tidy';
|
||||||
|
requires 'Params::Validate';
|
||||||
|
requires 'Params::Util';
|
||||||
|
requires 'Net::SSLeay';
|
||||||
|
requires 'HTML::HeadParser';
|
||||||
|
requires 'File::MimeInfo';
|
||||||
|
11
javascript_interpreter_xs/Makefile.PL
Normal file
11
javascript_interpreter_xs/Makefile.PL
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use ExtUtils::MakeMaker;
|
||||||
|
|
||||||
|
WriteMakefile(
|
||||||
|
NAME => 'Peertube::DL::Javascript',
|
||||||
|
VERSION => '0.1',
|
||||||
|
LIBS => ['-lduktape -l../src'],
|
||||||
|
INC => '-Iduktape -I../src/include',
|
||||||
|
XS => { 'javascript.xs' => 'javascript.o' },
|
||||||
|
OBJECT => '../src/javascript_builtins.o javascript.o',
|
||||||
|
LDFLAGS => '-Wl-t',
|
||||||
|
);
|
110
javascript_interpreter_xs/javascript.xs
Normal file
110
javascript_interpreter_xs/javascript.xs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
#define PERL_NO_GET_CONTEXT
|
||||||
|
#include "EXTERN.h"
|
||||||
|
#include "perl.h"
|
||||||
|
#include "XSUB.h"
|
||||||
|
#include "duktape.h"
|
||||||
|
#include "duk_config.h"
|
||||||
|
#include "javascript_builtins.h"
|
||||||
|
|
||||||
|
MODULE = Peertube::DL::Javascript PACKAGE = Peertube::DL::Javascript
|
||||||
|
PROTOTYPES: ENABLE
|
||||||
|
|
||||||
|
SV *
|
||||||
|
_duk_create_heap_default()
|
||||||
|
CODE:
|
||||||
|
duk_context *context = duk_create_heap_default();
|
||||||
|
if (context) {
|
||||||
|
duk_push_c_function(context, js_builtin_print, 1);
|
||||||
|
duk_put_global_string(context, "print");
|
||||||
|
RETVAL = newSVuv((size_t)context);
|
||||||
|
} else {
|
||||||
|
RETVAL = &PL_sv_undef;
|
||||||
|
}
|
||||||
|
OUTPUT:
|
||||||
|
RETVAL
|
||||||
|
|
||||||
|
SV *
|
||||||
|
_duk_eval_wrapper(SV *, SV *)
|
||||||
|
CODE:
|
||||||
|
if (!ST(0)) {
|
||||||
|
croak("Javascript context undefined.", 0);
|
||||||
|
}
|
||||||
|
duk_context *context = (duk_context *) SvUV(ST(0));
|
||||||
|
if (!context) {
|
||||||
|
croak("Javascript context null on function call.");
|
||||||
|
}
|
||||||
|
duk_idx_t top_index;
|
||||||
|
if ( (top_index = duk_get_top_index(context)) != DUK_INVALID_INDEX) {
|
||||||
|
duk_pop_n(context, top_index + 1);
|
||||||
|
}
|
||||||
|
duk_require_stack(context, 2);
|
||||||
|
if (!ST(1)) {
|
||||||
|
croak("Code to eval is undef.");
|
||||||
|
}
|
||||||
|
STRLEN code_len;
|
||||||
|
char *code = SvPV(ST(1), code_len);
|
||||||
|
duk_push_lstring(context, code, strlen(code));
|
||||||
|
if ( duk_peval(context) != 0 ) {
|
||||||
|
croak("Eval failed:\n%s\n%s\n", duk_safe_to_string(context, -1), duk_to_stacktrace(context, -1));
|
||||||
|
}
|
||||||
|
duk_size_t *lstring_len;
|
||||||
|
const char * lstring = duk_get_lstring(context, -1, lstring_len);
|
||||||
|
RETVAL = newSVpv(lstring, (size_t) lstring_len);
|
||||||
|
top_index = duk_get_top_index(context);
|
||||||
|
duk_pop_n(context, top_index+1);
|
||||||
|
OUTPUT:
|
||||||
|
RETVAL
|
||||||
|
|
||||||
|
SV *
|
||||||
|
_duk_call_function(SV *, SV *, ...)
|
||||||
|
CODE:
|
||||||
|
if (!ST(0)) {
|
||||||
|
croak("Context is undef.");
|
||||||
|
}
|
||||||
|
duk_context *context = (duk_context *) SvUV(ST(0));
|
||||||
|
if (!context) {
|
||||||
|
croak("Javascript context null on function call.");
|
||||||
|
}
|
||||||
|
STRLEN function_name_len;
|
||||||
|
if (!ST(1)) {
|
||||||
|
croak("Function name must be defined.");
|
||||||
|
}
|
||||||
|
char * function_name = SvPV(ST(1), function_name_len);
|
||||||
|
if ( function_name_len == 0 ) {
|
||||||
|
croak("Function name cannot be empty.");
|
||||||
|
}
|
||||||
|
duk_idx_t top_index;
|
||||||
|
if ( (top_index = duk_get_top_index(context)) != DUK_INVALID_INDEX ) {
|
||||||
|
duk_pop_n(context, top_index+1);
|
||||||
|
}
|
||||||
|
duk_require_stack(context, items+1);
|
||||||
|
(void) duk_get_global_lstring(context, function_name, strlen(function_name));
|
||||||
|
if (items > 2) {
|
||||||
|
for ( int i = 2; i < items; i++) {
|
||||||
|
SV *argument_SV = ST(i);
|
||||||
|
if (!argument_SV) {
|
||||||
|
croak("Argument %d undefined.", i);
|
||||||
|
}
|
||||||
|
STRLEN argument_len;
|
||||||
|
char *argument = SvPV(argument_SV, argument_len);
|
||||||
|
duk_push_lstring(context, argument, strlen(argument));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
duk_size_t *lstring_len;
|
||||||
|
duk_call(context, 1);
|
||||||
|
char * return_value = duk_get_lstring(context, -1, lstring_len);
|
||||||
|
if ( (top_index = duk_get_top_index(context)) != DUK_INVALID_INDEX ) {
|
||||||
|
duk_pop_n(context, top_index+1);
|
||||||
|
}
|
||||||
|
RETVAL = newSVpv(return_value, (size_t) lstring_len);
|
||||||
|
OUTPUT:
|
||||||
|
RETVAL
|
||||||
|
|
||||||
|
void
|
||||||
|
_duk_destroy_heap(SV *)
|
||||||
|
CODE:
|
||||||
|
duk_context *context = (duk_context *) SvUV(ST(0));
|
||||||
|
if (!context) {
|
||||||
|
croak("Cannot destroy something that is not a context.");
|
||||||
|
}
|
||||||
|
duk_destroy_heap(context);
|
@ -1,3 +1,4 @@
|
|||||||
|
## Please see file perltidy.ERR
|
||||||
package Peertube::DL::Downloaders;
|
package Peertube::DL::Downloaders;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
@ -5,10 +6,126 @@ use warnings;
|
|||||||
|
|
||||||
use feature 'say';
|
use feature 'say';
|
||||||
|
|
||||||
|
use Symbol 'gensym';
|
||||||
|
use IPC::Open3;
|
||||||
|
use File::Basename;
|
||||||
use JSON;
|
use JSON;
|
||||||
use Peertube::DL::Utils;
|
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
use Mojo::DOM;
|
use Mojo::DOM;
|
||||||
|
use File::MimeInfo;
|
||||||
|
|
||||||
|
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 $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
|
||||||
|
};
|
||||||
|
|
||||||
|
say Data::Dumper::Dumper $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 Data::Dumper::Dumper $format;
|
||||||
|
my $mime_type = $format->{mimeType} =~ s/;.*$//r;
|
||||||
|
say $mime_type;
|
||||||
|
my $extension = scalar File::MimeInfo::extensions($mime_type);
|
||||||
|
$extension = 'mp4' if $mime_type eq 'video/mp4';
|
||||||
|
say $url;
|
||||||
|
return {
|
||||||
|
url => $url,
|
||||||
|
filename => $microformat->{title}{simpleText} . '.' . $extension,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
options => { list_formats => 1 },
|
||||||
|
title => $microformat->{title}{simpleText},
|
||||||
|
description => $microformat->{description}{simpleText},
|
||||||
|
formats => [
|
||||||
|
map {
|
||||||
|
{
|
||||||
|
id => $_->{itag},
|
||||||
|
mimeType => $_->{mimeType}
|
||||||
|
}
|
||||||
|
} (
|
||||||
|
scalar @{ $ytInitialPlayerResponse->{adaptiveFormats} }
|
||||||
|
? @{ $ytInitialPlayerResponse->{adaptiveFormats} }
|
||||||
|
: (),
|
||||||
|
scalar @{ $ytInitialPlayerResponse->{formats} }
|
||||||
|
? @{ $ytInitialPlayerResponse->{formats} }
|
||||||
|
: ()
|
||||||
|
)
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub kjanime {
|
sub kjanime {
|
||||||
my $ua = shift;
|
my $ua = shift;
|
||||||
@ -33,7 +150,8 @@ sub kjanime {
|
|||||||
'X-Requested-With' => 'XMLHttpRequest',
|
'X-Requested-With' => 'XMLHttpRequest',
|
||||||
);
|
);
|
||||||
$dom = Mojo::DOM->new( $get_php_response->decoded_content );
|
$dom = Mojo::DOM->new( $get_php_response->decoded_content );
|
||||||
my $links = [ map { $_->attr('href') =~ s/^http:\/\//https:\/\//r } @{ $dom->find('a')->to_array } ];
|
my $links = [ map { $_->attr('href') =~ s/^http:\/\//https:\/\//r }
|
||||||
|
@{ $dom->find('a')->to_array } ];
|
||||||
return { options => { list => 1 }, urls => $links };
|
return { options => { list => 1 }, urls => $links };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +160,8 @@ sub kjanime_ch {
|
|||||||
my $response = shift;
|
my $response = shift;
|
||||||
my ($id) = $response->filename;
|
my ($id) = $response->filename;
|
||||||
say $id;
|
say $id;
|
||||||
my $param = join '', map { Peertube::DL::Utils::uri_decode("%$_") } unpack( "(A2)*", $id );
|
my $param = join '',
|
||||||
|
map { Peertube::DL::Utils::uri_decode("%$_") } unpack( "(A2)*", $id );
|
||||||
say $param;
|
say $param;
|
||||||
|
|
||||||
# FORMAT: KJA://70714B6E314943383135712B72582B70713768797A4B6E4D734D474775773D3D/1
|
# FORMAT: KJA://70714B6E314943383135712B72582B70713768797A4B6E4D734D474775773D3D/1
|
||||||
@ -58,7 +177,8 @@ sub kjanime_ch {
|
|||||||
$url_serie_php,
|
$url_serie_php,
|
||||||
'X-Requested-With' => 'XMLHttpRequest',
|
'X-Requested-With' => 'XMLHttpRequest',
|
||||||
);
|
);
|
||||||
my $download_data = JSON::decode_json( $response_serie_php->decoded_content );
|
my $download_data =
|
||||||
|
JSON::decode_json( $response_serie_php->decoded_content );
|
||||||
say Data::Dumper::Dumper $download_data;
|
say Data::Dumper::Dumper $download_data;
|
||||||
$download_data->{filename} = delete $download_data->{name};
|
$download_data->{filename} = delete $download_data->{name};
|
||||||
$download_data->{url} = delete $download_data->{link};
|
$download_data->{url} = delete $download_data->{link};
|
||||||
@ -75,9 +195,11 @@ sub gocdn {
|
|||||||
my $url_gocdn = 'https://streamium.xyz/gocdn.php?v=' . $id;
|
my $url_gocdn = 'https://streamium.xyz/gocdn.php?v=' . $id;
|
||||||
my $response_gocdn = $ua->get($url_gocdn);
|
my $response_gocdn = $ua->get($url_gocdn);
|
||||||
|
|
||||||
die $response_gocdn->status_line . ' from ' . $url_gocdn unless $response_gocdn->is_success;
|
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 $google_url =
|
||||||
|
JSON::decode_json( $response_gocdn->decoded_content )->{file};
|
||||||
|
|
||||||
my ($filename) = $response->base =~ s/^.*\///gr . '.mp4';
|
my ($filename) = $response->base =~ s/^.*\///gr . '.mp4';
|
||||||
|
|
||||||
@ -85,12 +207,13 @@ sub gocdn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub animeid {
|
sub animeid {
|
||||||
my $ua = shift;
|
my $ua = shift;
|
||||||
my $response = shift;
|
my $response = shift;
|
||||||
my @order = ( 'id', 'title', 'refer' );
|
my @order = ( 'id', 'title', 'refer' );
|
||||||
my $url = $response->base;
|
my $url = $response->base;
|
||||||
my $url_ajax = 'https://animeid.to/ajax.php?';
|
my $url_ajax = 'https://animeid.to/ajax.php?';
|
||||||
my ($get_params) = $response->decoded_content =~ /animeid\.to\/streaming\.php\?(.*?)"/;
|
my ($get_params) =
|
||||||
|
$response->decoded_content =~ /animeid\.to\/streaming\.php\?(.*?)"/;
|
||||||
$get_params = {
|
$get_params = {
|
||||||
map {
|
map {
|
||||||
my ( $key, $value ) = /^(.*?)=(.*)$/;
|
my ( $key, $value ) = /^(.*?)=(.*)$/;
|
||||||
@ -104,7 +227,10 @@ sub animeid {
|
|||||||
$get_params->{refer} = $url;
|
$get_params->{refer} = $url;
|
||||||
my $filename = $get_params->{title};
|
my $filename = $get_params->{title};
|
||||||
$url_ajax .= join '&',
|
$url_ajax .= join '&',
|
||||||
map { join '=', ( $_ => Peertube::DL::Utils::uri_encode( $get_params->{$_} ) ) } @order;
|
map {
|
||||||
|
join '=',
|
||||||
|
( $_ => Peertube::DL::Utils::uri_encode( $get_params->{$_} ) )
|
||||||
|
} @order;
|
||||||
say 'Getting video location from: ' . $url_ajax;
|
say 'Getting video location from: ' . $url_ajax;
|
||||||
my $ajax_data = $ua->get(
|
my $ajax_data = $ua->get(
|
||||||
$url_ajax,
|
$url_ajax,
|
||||||
@ -115,8 +241,9 @@ sub animeid {
|
|||||||
say "Decoding json... $ajax_data";
|
say "Decoding json... $ajax_data";
|
||||||
$ajax_data = JSON::decode_json $ajax_data;
|
$ajax_data = JSON::decode_json $ajax_data;
|
||||||
die 'No video source found.' if ( !defined $ajax_data->{source} );
|
die 'No video source found.' if ( !defined $ajax_data->{source} );
|
||||||
my $download_redirect_url = $ajax_data->{source}[0]{file} // die "No url found.";
|
my $download_redirect_url = $ajax_data->{source}[0]{file}
|
||||||
my $extension = $ajax_data->{source}[0]{type} // die "No extension found.";
|
// die "No url found.";
|
||||||
|
my $extension = $ajax_data->{source}[0]{type} // die "No extension found.";
|
||||||
$filename .= ".$extension";
|
$filename .= ".$extension";
|
||||||
say "Getting redirect to download url from $download_redirect_url...";
|
say "Getting redirect to download url from $download_redirect_url...";
|
||||||
my $video_response = $ua->get(
|
my $video_response = $ua->get(
|
||||||
|
52
lib/Peertube/DL/Javascript.pm
Executable file
52
lib/Peertube/DL/Javascript.pm
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
package Peertube::DL::Javascript;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use feature 'say';
|
||||||
|
|
||||||
|
use XSLoader;
|
||||||
|
use Data::Dumper;
|
||||||
|
|
||||||
|
XSLoader::load();
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $class = shift;
|
||||||
|
my $self = bless {}, $class;
|
||||||
|
$self->{___ContextPrivateDONOTTOUCH} =
|
||||||
|
Peertube::DL::Javascript::_duk_create_heap_default();
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub evalJS {
|
||||||
|
my $self = shift;
|
||||||
|
my $context = $self->___ContextDONOTUSE;
|
||||||
|
my $code = shift;
|
||||||
|
return Peertube::DL::Javascript::_duk_eval_wrapper( $context, $code );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub callJSFunction {
|
||||||
|
my $self = shift;
|
||||||
|
my $function = shift;
|
||||||
|
my @function_arguments = @_;
|
||||||
|
my $context = $self->___ContextDONOTUSE;
|
||||||
|
return Peertube::DL::Javascript::_duk_call_function(
|
||||||
|
$context,
|
||||||
|
$function,
|
||||||
|
@function_arguments
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub ___ContextDONOTUSE {
|
||||||
|
my $self = shift;
|
||||||
|
return $self->{___ContextPrivateDONOTTOUCH};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub DESTROY {
|
||||||
|
my $self = shift;
|
||||||
|
my $context = $self->___ContextDONOTUSE;
|
||||||
|
Peertube::DL::Javascript::_duk_destroy_heap($context);
|
||||||
|
undef $self;
|
||||||
|
}
|
||||||
|
1;
|
@ -11,6 +11,7 @@ use Peertube::DL::Downloaders;
|
|||||||
sub getDownloadDataFromURL {
|
sub getDownloadDataFromURL {
|
||||||
my $url_origen = shift;
|
my $url_origen = shift;
|
||||||
my $ua = Peertube::DL::URLHandler::generateUA();
|
my $ua = Peertube::DL::URLHandler::generateUA();
|
||||||
|
my $options = shift;
|
||||||
$ua->set_redirect_ok(1);
|
$ua->set_redirect_ok(1);
|
||||||
my $response = $ua->get($url_origen);
|
my $response = $ua->get($url_origen);
|
||||||
my %handlers = (
|
my %handlers = (
|
||||||
@ -18,20 +19,23 @@ sub getDownloadDataFromURL {
|
|||||||
animeid => { reg => qr/animeid\.to\/streaming\.php\?/, subr => \&Peertube::DL::Downloaders::animeid },
|
animeid => { reg => qr/animeid\.to\/streaming\.php\?/, subr => \&Peertube::DL::Downloaders::animeid },
|
||||||
kjanime => {
|
kjanime => {
|
||||||
reg => qr/kjanime - Anime en formato ligero y HQ - kjanime/,
|
reg => qr/kjanime - Anime en formato ligero y HQ - kjanime/,
|
||||||
subr => \&Peertube::DL::Downloaders::kjanime
|
subr => \&Peertube::DL::Downloaders::kjanime,
|
||||||
},
|
},
|
||||||
kjanime_ch => {
|
kjanime_ch => {
|
||||||
reg => qr/Link de descarga . kjanime/,
|
reg => qr/Link de descarga . kjanime/,
|
||||||
subr => \&Peertube::DL::Downloaders::kjanime_ch
|
subr => \&Peertube::DL::Downloaders::kjanime_ch,
|
||||||
|
},
|
||||||
|
youtube => {
|
||||||
|
reg => qr/ytInitialPlayerResponse = \{/,
|
||||||
|
subr => \&Peertube::DL::Downloaders::youtube,
|
||||||
},
|
},
|
||||||
,
|
|
||||||
);
|
);
|
||||||
$ua->set_redirect_ok(0);
|
$ua->set_redirect_ok(0);
|
||||||
my $handled = 0;
|
my $handled = 0;
|
||||||
my $download_data;
|
my $download_data;
|
||||||
for my $x ( keys %handlers ) {
|
for my $x ( keys %handlers ) {
|
||||||
if ( $response->decoded_content =~ m/$handlers{$x}{reg}/ ) {
|
if ( $response->decoded_content =~ m/$handlers{$x}{reg}/ ) {
|
||||||
eval { $download_data = $handlers{$x}{subr}->( $ua, $response ); };
|
eval { $download_data = $handlers{$x}{subr}->( $ua, $response, $options ); };
|
||||||
if ($@) {
|
if ($@) {
|
||||||
warn $@;
|
warn $@;
|
||||||
} else {
|
} else {
|
||||||
@ -46,15 +50,28 @@ sub getDownloadDataFromURL {
|
|||||||
unless $handled;
|
unless $handled;
|
||||||
die "Download data not defined" unless defined $download_data;
|
die "Download data not defined" unless defined $download_data;
|
||||||
die "Download data not hashref" unless ref($download_data) eq 'HASH';
|
die "Download data not hashref" unless ref($download_data) eq 'HASH';
|
||||||
if ( defined $download_data->{options}{list}
|
if ( defined $download_data->{options} ) {
|
||||||
&& $download_data->{options}{list} )
|
if ( defined $download_data->{options}{list}
|
||||||
{
|
&& $download_data->{options}{list} )
|
||||||
say 'Reproduction list detected.';
|
{
|
||||||
die 'No url list.'
|
say 'Reproduction list detected.';
|
||||||
unless defined $download_data->{urls};
|
die 'No url list.'
|
||||||
die 'Urls is not an array'
|
unless defined $download_data->{urls};
|
||||||
unless ref $download_data->{urls} eq 'ARRAY';
|
die 'Urls is not an array'
|
||||||
return $download_data;
|
unless ref $download_data->{urls} eq 'ARRAY';
|
||||||
|
return $download_data;
|
||||||
|
}
|
||||||
|
if ( defined $download_data->{options}{list_formats} && $download_data->{options}{list_formats} ) {
|
||||||
|
say 'List of formats retrieved.';
|
||||||
|
die "No title." unless defined $download_data->{title};
|
||||||
|
die "No description." unless defined $download_data->{description};
|
||||||
|
die "No formats available." unless defined $download_data->{formats};
|
||||||
|
die "Formats is not an arrayref." unless ref $download_data->{formats} eq 'ARRAY';
|
||||||
|
say "The video title is $download_data->{title}.";
|
||||||
|
say "The video description is $download_data->{description}.";
|
||||||
|
say "The available formats are: @{[Data::Dumper::Dumper $download_data->{formats}]}.";
|
||||||
|
return $download_data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
die "Filename not defined" unless exists $download_data->{filename} && $download_data->{filename};
|
die "Filename not defined" unless exists $download_data->{filename} && $download_data->{filename};
|
||||||
die "Download url not defined" unless exists $download_data->{url} && defined $download_data->{url};
|
die "Download url not defined" unless exists $download_data->{url} && defined $download_data->{url};
|
||||||
|
11
src/Makefile.PL
Normal file
11
src/Makefile.PL
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use ExtUtils::MakeMaker;
|
||||||
|
|
||||||
|
WriteMakefile(
|
||||||
|
NAME => 'Peertube::DL::SRC',
|
||||||
|
VERSION => '0.1',
|
||||||
|
LIBS => ['-lduktape'],
|
||||||
|
INC => '-Iduktape -I./include',
|
||||||
|
C => [ 'javascript_builtins.c', ],
|
||||||
|
OBJECT => '${O_FILES}',
|
||||||
|
LDFLAGS => '-Wl-t',
|
||||||
|
);
|
5
src/include/javascript_builtins.h
Normal file
5
src/include/javascript_builtins.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "duktape.h"
|
||||||
|
#include "duk_config.h"
|
||||||
|
|
||||||
|
duk_ret_t js_builtin_print(duk_context *context);
|
9
src/javascript_builtins.c
Normal file
9
src/javascript_builtins.c
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "duktape.h"
|
||||||
|
#include "duk_config.h"
|
||||||
|
|
||||||
|
duk_ret_t js_builtin_print(duk_context *context) {
|
||||||
|
const char * to_print = duk_get_string(context, 0);
|
||||||
|
printf("%s", to_print);
|
||||||
|
return 1;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user