Compare commits

..

No commits in common. "master" and "master" have entirely different histories.

36 changed files with 156 additions and 2399 deletions

View File

@ -1,4 +1,4 @@
@sergiotarxz:sergiotarxz.hopto.org (Matrix address.)
@ale@ale.manalejandro.com/@ale@mastodon.madrid (Fediverse accounts.)
@destinyuwu:sergiotarxz.hopto.org (Matrix address.)
@abnazhor (Telegram.)

View File

@ -1,7 +0,0 @@
{
"version" : "0.0.1",
"name" : "Peertube-DL",
"author" : "sergiotarxz",
"abstract" : "A module for downloading videos from various webpages",
"license" : "AGPLv3"
}

View File

@ -43,9 +43,6 @@ if ( defined $download_data->{options}{list} && $download_data->{options}{list}
say "The urls are:\n" . join "\n", @$urls;
}
} elsif ( defined $download_data->{options}{list_formats} && $download_data->{options}{list_formats} ) {
say 'Listing formats available.';
delete $download_data->{options};
say "The available formats are: @{[Data::Dumper::Dumper $download_data]}";
exit 0;
} else {
downloadVideo( $ua, $download_data );

View File

@ -1,24 +0,0 @@
#!/sbin/openrc-run
# Requires OpenRC >= 0.35
directory=/var/lib/peertubedl
PATH="/var/lib/peertubedl/perl5/bin${PATH:+:${PATH}}"; export PATH;
PERL5LIB="/var/lib/peertubedl/perl5/lib/perl5${PERL5LIB:+:${PERL5LIB}}"; export PERL5LIB;
PERL_LOCAL_LIB_ROOT="/var/lib/peertubedl/perl5${PERL_LOCAL_LIB_ROOT:+:${PERL_LOCAL_LIB_ROOT}}"; export PERL_LOCAL_LIB_ROOT;
PERL_MB_OPT="--install_base \"/var/lib/peertubedl/perl5\""; export PERL_MB_OPT;
PERL_MM_OPT="INSTALL_BASE=/var/lib/peertubedl/perl5"; export PERL_MM_OPT;
command=/var/lib/peertubedl/perl5/bin/peertube-dl-hypnotoad
command_args=""
command_user=peertubedl:peertubedl
pidfile="/var/lib/peertubedl/peertube-dl-web.pid"
export PIDFILE=$pidfile
depend() {
need postgresql
}

View File

@ -298,10 +298,7 @@ sub animeid {
filename => $filename,
};
} else {
return {
url => $download_redirect_url,
filename => $filename,
}
die 'Getting redirect failed because: ' . $video_response->status_line;
}
}
1;

View File

@ -67,6 +67,9 @@ sub getDownloadDataFromURL {
die "No description." unless defined $download_data->{description};
die "No formats available." unless defined $download_data->{formats};
die "Formats is not a hash." unless ref $download_data->{formats} eq 'HASH';
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;
}
}

View File

@ -5,6 +5,5 @@
clients => 3,
proxy => 1,
pid_file => $ENV{PIDFILE} || '/var/run/peertube-dl-web.pid',
},
theme => 'new_look_default',
}
};

View File

@ -1,321 +0,0 @@
#!/usr/bin/env perl
use strict;
use warnings;
use Test::Most tests => 5;
use Digest::SHA qw/sha512_hex/;
use Data::Dumper;
BEGIN {
use_ok 'Peertube::DL::URLHandler';
}
## Tests WITH signatures.
{
my $download_data = Peertube::DL::URLHandler::getDownloadDataFromURL(
'https://www.youtube.com/watch?v=nqc4t4029as');
my $download_data_expected = {
'formats' => {
'audio_formats' => [
{
'mimeType' => 'audio/webm; codecs="opus"',
'quality' => 'tiny',
'audioSampleRate' => 48000,
'bitrate' => 133750,
'id' => 251
},
{
'bitrate' => 67550,
'id' => 250,
'mimeType' => 'audio/webm; codecs="opus"',
'quality' => 'tiny',
'audioSampleRate' => 48000
},
{
'bitrate' => 51104,
'id' => 249,
'mimeType' => 'audio/webm; codecs="opus"',
'audioSampleRate' => 48000,
'quality' => 'tiny'
}
],
'video_formats' => [
{
'qualityLabel' => 1080,
'mimeType' => 'video/mp4; codecs="avc1.640028"',
'quality' => 'hd1080',
'id' => 137,
'bitrate' => 1610654
},
{
'quality' => 'hd1080',
'mimeType' => 'video/webm; codecs="vp9"',
'qualityLabel' => 1080,
'bitrate' => 1552727,
'id' => 248
},
{
'bitrate' => 1074995,
'id' => 399,
'mimeType' => 'video/mp4; codecs="av01.0.08M.08"',
'qualityLabel' => 1080,
'quality' => 'hd1080'
},
{
'id' => 247,
'bitrate' => 687059,
'quality' => 'hd720',
'mimeType' => 'video/webm; codecs="vp9"',
'qualityLabel' => 720
},
{
'quality' => 'hd720',
'qualityLabel' => 720,
'mimeType' => 'video/mp4; codecs="av01.0.05M.08"',
'id' => 398,
'bitrate' => 659074
},
{
'mimeType' => 'video/mp4; codecs="avc1.4d401f"',
'qualityLabel' => 720,
'quality' => 'hd720',
'id' => 136,
'bitrate' => 579992
},
{
'id' => 244,
'bitrate' => 421009,
'qualityLabel' => 480,
'mimeType' => 'video/webm; codecs="vp9"',
'quality' => 'large'
},
{
'quality' => 'large',
'mimeType' => 'video/mp4; codecs="av01.0.04M.08"',
'qualityLabel' => 480,
'id' => 397,
'bitrate' => 385094
},
{
'mimeType' => 'video/mp4; codecs="avc1.4d401e"',
'qualityLabel' => 480,
'quality' => 'large',
'id' => 135,
'bitrate' => 353336
},
{
'bitrate' => 645178,
'id' => 18,
'mimeType' => 'video/mp4; codecs="avc1.42001E, mp4a.40.2"',
'quality' => 'medium',
'qualityLabel' => 360,
'audioSampleRate' => '44100'
},
{
'quality' => 'medium',
'qualityLabel' => 360,
'mimeType' => 'video/webm; codecs="vp9"',
'bitrate' => 281570,
'id' => 243
},
{
'id' => 134,
'bitrate' => 241943,
'quality' => 'medium',
'qualityLabel' => 360,
'mimeType' => 'video/mp4; codecs="avc1.4d401e"'
},
{
'bitrate' => 234609,
'id' => 396,
'quality' => 'medium',
'qualityLabel' => 360,
'mimeType' => 'video/mp4; codecs="av01.0.01M.08"'
},
{
'mimeType' => 'video/webm; codecs="vp9"',
'qualityLabel' => 240,
'quality' => 'small',
'id' => 242,
'bitrate' => 160233
},
{
'qualityLabel' => 240,
'mimeType' => 'video/mp4; codecs="avc1.4d4015"',
'quality' => 'small',
'bitrate' => 130312,
'id' => 133
},
{
'id' => 395,
'bitrate' => 121695,
'mimeType' => 'video/mp4; codecs="av01.0.00M.08"',
'qualityLabel' => 240,
'quality' => 'small'
},
{
'id' => 278,
'bitrate' => 86048,
'quality' => 'tiny',
'qualityLabel' => 144,
'mimeType' => 'video/webm; codecs="vp9"'
},
{
'bitrate' => 77340,
'id' => 160,
'qualityLabel' => 144,
'mimeType' => 'video/mp4; codecs="avc1.4d400c"',
'quality' => 'tiny'
},
{
'mimeType' => 'video/mp4; codecs="av01.0.00M.08"',
'qualityLabel' => 144,
'quality' => 'tiny',
'bitrate' => 63369,
'id' => 394
}
]
},
'options' => { 'list_formats' => 1 },
'description' =>
"Tras el \x{e9}xito de Zeus, Hades, Poseid\x{f3}n y Dem\x{e9}ter, hoy os traemos la canci\x{f3}n de otra de sus hermanas, \x{a1}Hestia! \x{bf}A qu\x{e9} esperas para descubrir la historia de esta diosa de la mitolog\x{ed}a griega en un nuevo Destripando la Historia?
\x{a1}Suscr\x{ed}bete a nuestro canal!
http://bit.ly/pascuyrodri
\x{a1}Escucha esta y otras canciones en SPOTIFY!
https://spoti.fi/2SY6zni
\x{a1}Consigue nuestras camisetas, chapas y l\x{e1}minas!
https://www.destripandolahistoria.es
*Solo con env\x{ed}o a Espa\x{f1}a peninsular.
\x{a1}Tambi\x{e9}n ten\x{e9}is parte de nuestro merchandising en Teespring!
(Con env\x{ed}os internacionales)
https://www.teespring.com/stores/destripandolahistoria
\x{a1}Puedes seguir a Destripando la Historia en Facebook, Instagram y Twitter! Publicamos contenido extra como dibujos, v\x{ed}deos cantando en directo o tonter\x{ed}as absurdas, ya nos conoc\x{e9}is...
http://www.instagram.com/destripandolahistoria
http://www.facebook.com/destripandolahistoria
https://twitter.com/pascuyrodri
\x{a1}Hola a todos!
\x{a1}La cuarentena no ha podido con nosotros y aqu\x{ed} ten\x{e9}is un nuevo v\x{ed}deo! Esperamos que est\x{e9}is todos bien y que lo disfrut\x{e9}is. Si hab\x{e9}is le\x{ed}do hasta aqu\x{ed}, como venganza porque Pascu ha metido una foto m\x{ed}a al principio del v\x{ed}deo, os pido que pong\x{e1}is en los comentarios: \x{201c}Pascu, baja la tapa del v\x{e1}ter\x{201d}.
Rodri
\x{a1}Recordad que pod\x{e9}is seguirnos tambi\x{e9}n en nuestras redes sociales personales!
http://www.instagram.com/rodrigoseptienmusic
https://www.instagram.com/alv_pascu/
https://twitter.com/var_Stark
https://twitter.com/RodrigoSeptien",
'title' => 'Hestia | Destripando la Historia'
};
is_deeply $download_data, $download_data_expected,
'Testing listing formats with obfuscated signatures.';
}
{
my $download_data = Peertube::DL::URLHandler::getDownloadDataFromURL(
'https://www.youtube.com/watch?v=nqc4t4029as', { format => 18 } );
my $ua = Peertube::DL::URLHandler::generateUA();
my $downloaded_video_checksum =
sha512_hex( $ua->get( $download_data->{url} )->decoded_content );
my $expected_checksum =
'a59f5f1e582bd558ef4e928d7edfd942452aff5084218a3470da946d73c94f28e102773d5b19f47c6224492a82e6c7072a47aae87333bcfeda457a33626c92e7';
is $downloaded_video_checksum, $expected_checksum,
'Testing downloading with obfuscated signatures.';
}
## WITHOUT signature.
{
my $expected_download_data = {
'description' =>
'Richard Stallman en Ecuador, cantando el temita, del free software, grabado por Julian Coccia.',
'formats' => {
'audio_formats' => [
{
'bitrate' => 139941,
'id' => 251,
'quality' => 'tiny',
'audioSampleRate' => 48000,
'mimeType' => 'audio/webm; codecs="opus"'
},
{
'bitrate' => 72446,
'id' => 250,
'audioSampleRate' => 48000,
'quality' => 'tiny',
'mimeType' => 'audio/webm; codecs="opus"'
},
{
'bitrate' => 55976,
'id' => 249,
'quality' => 'tiny',
'audioSampleRate' => 48000,
'mimeType' => 'audio/webm; codecs="opus"'
}
],
'video_formats' => [
{
'id' => 18,
'bitrate' => 367576,
'audioSampleRate' => '44100',
'quality' => 'small',
'mimeType' => 'video/mp4; codecs="avc1.42001E, mp4a.40.2"',
'qualityLabel' => 240
},
{
'bitrate' => 244690,
'id' => 133,
'quality' => 'small',
'qualityLabel' => 240,
'mimeType' => 'video/mp4; codecs="avc1.4d400d"'
},
{
'mimeType' => 'video/webm; codecs="vp9"',
'quality' => 'small',
'qualityLabel' => 240,
'id' => 242,
'bitrate' => 161442
},
{
'bitrate' => 112818,
'id' => 160,
'qualityLabel' => 144,
'quality' => 'tiny',
'mimeType' => 'video/mp4; codecs="avc1.4d400c"'
},
{
'mimeType' => 'video/webm; codecs="vp9"',
'quality' => 'tiny',
'qualityLabel' => 144,
'id' => 278,
'bitrate' => 70172
}
]
},
'title' => 'Richard Stallman Free software Song'
};
my $download_data = Peertube::DL::URLHandler::getDownloadDataFromURL(
'https://www.youtube.com/watch?v=9sJUDx7iEJw');
delete $download_data->{options};
is_deeply( $download_data, $expected_download_data,
'Testing listing signatures without obfuscated signatures.' );
}
{
my $download_data = Peertube::DL::URLHandler::getDownloadDataFromURL(
'https://www.youtube.com/watch?v=9sJUDx7iEJw', { format => 18 } );
my $ua = Peertube::DL::URLHandler::generateUA();
my $downloaded_video_checksum =
sha512_hex( $ua->get( $download_data->{url} )->decoded_content );
my $expected_checksum =
'16343940a5473a2ac3f485d11b1e920c974a4f506859368f669ad8af4a30b885b41c8ba301364997fc2ae50bb41d9d167768bcd9bfb4a51d76767ec165b744da';
is $downloaded_video_checksum, $expected_checksum,
'Testing downloading without obfuscated signatures.';
}

View File

@ -1,276 +1,189 @@
body {
height: 99.9%;
margin: 0;
padding: 0;
}
height: 98vh;
a {
color: blue;
}
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
a:hover,a:focus {
text-decoration: underline;
}
#video {
width: 100%;
margin: 3px;
}
#modal-video-container {
display: none;
background: white;
position: fixed;
top: 50%;
left: 50%;
height: 100%;
width: 100%;
transform: translate(-50%, -50%);
border: black 1px solid;
}
#modal-video-container.active {
display: block;
}
.video-container-bar {
display: flex;
justify-content: right;
}
#close-and-reset-video-container {
margin-top: 0.25rem;
margin-right: 0.25rem;
border: 1px solid black;
}
#close-and-reset-video-container:hover:#close-and-reset-video-container:focus {
background: black;
color: white;
}
#download-video-container {
margin: 10px;
justify-content: center;
}
#download-video-container a {
display: none;
padding-left: 5px;
padding-right: 5px;
padding-top: 5px;
padding-bottom: 5px;
width: 100%;
background: #0a0;
border-radius: 5px;
font-size: 30px;
color: white;
height: 30px;
}
#download-video-container a.active {
display: block;
}
#download-video-container a embed {
height: 30px;
}
#video-container {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
height: 100%;
}
.block {
display: block;
background-color: #111827;
}
.application-container {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
text-align: center;
height: 100%;
background: #000;
color: white;
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
border-radius: 0.3rem;
background-color: #1f2937;
padding: 1.5rem 3rem 1.5rem 3rem;
}
#download-form {
display: flex;
justify-content: center;
flex-direction: column;
.application-container form {
display: flex;
flex-flow: column;
justify-content: center;
width: 100%;
padding: 1rem;
}
#download-form-button {
margin-top: 5px;
height: 50px;
font-size: 1.5rem;
background: #fff;
color: black;
border: none;
.application-container h2 {
color: #ffffff;
font-weight: 400;
font-size: 1.6rem;
}
h2 {
font-size: 2rem;
.application-container input {
padding: 1rem;
color: #ffffff;
background-color: #374151;
border-radius: 0.2rem;
border: 1px solid transparent;
}
#modal-loading {
display: none;
position: fixed;
top: 50%;
left: 50%;
height: 100%;
width: 100%;
transform: translate(-50%, -50%);
border: black 1px solid;
justify-content: right;
align-items: center;
.application-container button {
margin-top: 1rem;
color: #ffffff;
font-weight: bold;
background-color: #059669;
border-radius: 0.2rem;
border: 1px solid transparent;
padding: 0.5rem 1rem 0.5rem 1rem;
}
#modal-loading.active {
display: flex;
.application-container button,
.application-container input {
font-size: 0.9rem;
}
#modal-loading embed {
width: 10%;
.application-container.active {
display: flex;
}
#poping-notice {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: black 1px solid;
width: 91%;
background: white;
padding: 10px;
border-radius: 15px;
max-height: 95%;
overflow-y: scroll;
}
position: absolute;
display: none;
width: 40%;
padding: 3rem;
border-radius: 0.3rem;
background-color: #374151;
color: white;
}
#poping-notice.active {
display: block;
#poping-notice-content a {
text-decoration: none;
color: #10b981;
}
#poping-notice-container-bar {
display: flex;
justify-content: center;
font-size: 5rem;
display: flex;
justify-content: center;
}
#close-poping-notice {
width: 150px;
height: 150px;
align-items: center;
display: flex;
text-align: center;
justify-content: center;
border-radius: 50%;
background: #0f0;
color: black;
text-decoration: none;
background-color: #059669;
padding: 0.5rem 5rem 0.5rem 5rem;
border-radius: 0.3rem;
text-decoration: none;
font-weight: bolder;
color: white;
}
#close-poping-notice:hover,#close-poping-notice:focus {
background: black;
color: white;
#modal-video-container {
position: absolute;
display: none;
height: 100vh;
width: 100%;
flex-flow: column;
align-items: center;
background-color: #111827;
}
.video-container-bar {
width: 95%;
height: 2rem;
padding: 1rem;
display: flex;
justify-content: end;
}
.video-container-bar a {
padding: 1rem;
border-radius: 0.3rem;
display: flex;
align-items: center;
justify-content: center;
background-color: #dc2626;
}
#modal-video-container > #block {
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
}
#download-video-container {
padding: 2rem;
display: flex;
align-items: center;
justify-content: center;
}
#download-video-prepare,
#download-video {
display: none;
border-radius: 0.3rem;
background-color: #059669;
text-decoration: none;
color: white;
user-select: none;
cursor: pointer;
}
#download-video-loading.active {
height: 2rem;
}
#download-video-container a.active,
#download-video-loading.active {
display: block;
padding: 0.5rem 2rem 0.5rem 2rem;
}
#modal-loading,
#modal-format-selector {
display: none;
background: white;
position: fixed;
top: 50%;
left: 50%;
height: 100%;
width: 100%;
transform: translate(-50%, -50%);
border: black 1px solid;
flex-direction: column;
overflow-y: scroll;
display: none;
}
#modal-format-selector.active {
display: flex;
#poping-notice.active,
#modal-video-container.active {
--tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
0 4px 6px -2px rgba(0, 0, 0, 0.05);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
#modal-format-selector > h2 {
text-align: center;
width: 100%;
#poping-notice.active {
display: block;
}
#modal-format-selector > p {
margin-left: 2rem;
width: 100%;
}
#modal-format-selector .format-list {
box-sizing: border-box;
background: #fff;
margin: 2rem;
}
#close-modal-format-selector {
margin-top: 0.50rem;
margin-right: 0.50rem;
border: 1px solid black;
background: grey;
color: white;
width: 25px;
height: 25px;
text-align: center;
font-size: 20px;
font-weight: bold;
}
#close-modal-format-selector:hover,#close-modal-format-selector:focus {
background: black;
}
.format-list > div {
width: 100%;
display: grid;
grid-auto-columns: 50%;
grid-template-areas: "a a";
}
.format-list > div > a {
border: 1px solid black;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
padding-right: 5%;
padding-left: 5%;
text-decoration: none;
color: black;
background: #eee;
overflow-wrap: anywhere;
}
.format-list > div > a:hover {
background: black;
color: white;
}
.format-list > div > a:after {
padding-bottom: 100%;
display: block;
content: "";
}
div.video-formats a {
background: #f00;
}
@media (min-width: 668px) {
#poping-notice {
width: 629px;
}
}
#modal-video-container.active {
display: flex;
}

View File

@ -1,327 +0,0 @@
body {
height: 98vh;
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
background-color: #FF69B4;
}
.application-container {
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
border-radius: 0.3rem;
background-color: #C71585;
padding: 1.5rem 3rem 1.5rem 3rem;
}
.application-container form {
display: flex;
flex-flow: column;
justify-content: center;
width: 100%;
padding: 1rem;
}
.application-container h2 {
color: #ffffff;
font-weight: 400;
font-size: 1.6rem;
}
.application-container input {
padding: 1rem;
color: #C71585;
background-color: #FFB6C1;
border-radius: 0.2rem;
border: 1px solid transparent;
}
.application-container button {
margin-top: 1rem;
color: #ffffff;
font-weight: bold;
background-color: #059669;
border-radius: 0.2rem;
border: 1px solid transparent;
padding: 0.5rem 1rem 0.5rem 1rem;
}
.application-container button:hover,.application-container button:focus {
background-color: #059;
}
.application-container button,
.application-container input {
font-size: 0.9rem;
}
.application-container.active {
display: flex;
}
#poping-notice-content a {
text-decoration: none;
color: lime;
}
#poping-notice-content a:hover,#poping-notice-content a:focus {
color: aqua;
}
#poping-notice-container-bar {
display: flex;
justify-content: center;
}
#close-poping-notice {
background-color: #059669;
padding: 0.5rem 5rem 0.5rem 5rem;
border-radius: 0.3rem;
text-decoration: none;
font-weight: bolder;
color: white;
}
#close-poping-notice:hover,#close-poping-notice:focus {
background-color: #059;
}
.modal {
position: fixed;
display: none;
color: white;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#poping-notice {
padding: 3rem;
border-radius: 0.3rem;
background-color: #C71585;
color: white;
overflow-y: scroll;
width: 94%;
max-height: 70%;
flex-direction: column;
scrollbar-width: none;
}
#modal-video-container,#modal-format-selector {
height: 100%;
width: 100%;
flex-flow: column;
align-items: center;
background-color: #FF69B4;
}
#modal-format-selector h2 {
width: 100%;
}
#modal-format-selector p {
width: 100%;
}
.video-container-bar {
width: 99%;
height: 2rem;
padding-top: 1rem;
padding-bottom: 1rem;
display: flex;
justify-content: end;
margin-bottom: 1rem;
}
.video-container-bar a {
padding: 1rem;
margin-right: 1rem;
border-radius: 0.3rem;
display: flex;
align-items: center;
justify-content: center;
background-color: #dc2626;
}
.video-container-bar a:hover,.video-container-bar a:focus {
color: white;
background-color: grey;
}
#modal-video-container > #block {
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
}
#download-video-container {
padding: 2rem;
display: flex;
align-items: center;
justify-content: center;
}
.button-download {
display: none;
border-radius: 0.3rem;
background-color: #059669;
text-decoration: none;
color: white;
user-select: none;
cursor: pointer;
height: 30px;
padding: 0 2rem;
}
.button-download:hover,.button-download:focus {
background-color: #059;
}
.button-download embed {
height: 100%;
}
#download-video-loading.active {
height: 2rem;
}
#download-video-container .button-download.active {
display: flex;
align-items: center;
}
#modal-loading {
display: none;
}
.modal {
--tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
0 4px 6px -2px rgba(0, 0, 0, 0.05);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
overflow-y: scroll;
}
#poping-notice.active {
display: flex;
}
.modal.active {
display: flex;
scrollbar-width: none;
}
.modal.active.active::-webkit-scrollbar {
display: none;
}
#modal-loading {
display: none;
position: fixed;
top: 50%;
left: 50%;
height: 100%;
width: 100%;
transform: translate(-50%, -50%);
border: black 1px solid;
justify-content: right;
align-items: center;
}
#modal-loading.active {
display: flex;
}
#modal-loading embed {
width: 10%;
}
#video {
width: 100%;
max-width: 636px;
}
#video-container {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
.format-list > div {
display: grid;
grid-column-gap: 10px;
grid-row-gap: 10px;
grid-template-areas: "a";
grid-auto-columns: 99%;
margin: 1rem;
}
.format-list > div > a {
border: 1px solid black;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
padding-right: 5%;
padding-left: 5%;
text-decoration: none;
border-radius: 0.5rem;
background-color: #059669;
overflow-wrap: anywhere;
}
.format-list > div > a:focus,.format-list > div > a:hover {
background-color: #059;
}
.format-list > div > a:after {
padding-bottom: 100%;
display: block;
content: "";
}
.scale {
height: 1em;
width; 1em;
}
@media (min-width: 600px) {
.format-list > div {
grid-template-areas: "a a";
grid-auto-columns: 49.75%;
}
}
@media (min-width: 805px) {
.format-list > div {
grid-template-areas: "a a a";
grid-auto-columns: 33%;
}
}
@media (min-width: 812px) {
#poping-notice {
width: 40%;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -1,76 +0,0 @@
<html>
<head>
<script src="js/peertube-dl-web.js?v=1"></script>
<link rel="stylesheet" href="css/index.css?v=4"></script>
<link rel="icon" type="image/png" href="img/peertube-dl-logo.png"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Peertube-dl Web</title>
<meta name="description" content="Webpage intended to download videos from various webpages for fair use."/>
<meta property="og:type" content="website" />
<meta property="og:title" content="Peertube-dl Web"/>
<meta property="og:site_name" content="Peertube-dl"/>
<meta property="og:description" content="Webpage intended to download videos from various webpages for fair use."/>
<meta property="og:image" content="./img/peertube-dl-logo-fullsize.png"/>
</head>
<body>
<div class="application-container">
<h2>Peertube-dl Web Application</h2>
<form id="download-form">
<input class="block" type="text" id="download-form-url" placeholder="Introduce the url you want to download."/>
<button class="block" id="download-form-button" >Fetch from api</button>
</form>
</div>
<div id="modal-format-selector" class="modal">
<div class="video-container-bar">
<a id="close-modal-format-selector">x</a>
</div>
<h2>Example video</h2>
<p>Example description</p>
<div class="format-list">
<h3>Video Formats.</h3>
<div class="video-formats">
</div>
<h3>Audio Formats.</h3>
<div class="audio-formats">
</div>
</div>
</div>
<div id="modal-loading">
<embed src="img/spinner.svg"/>
</div>
<div id="modal-video-container" class="modal">
<div class="video-container-bar">
<a id="close-and-reset-video-container">x</a>
</div>
<div id="video-container">
<div class="block">
<video id="video" controls="controls"></video>
</div>
<div id="download-video-container" class="block">
<a id="download-video-prepare" class="button-download active">Prepare download</a>
<a id="download-video-loading" class="button-download"><embed src="img/spinner.svg"/></a>
<a id="download-video" class="button-download">Download</a>
</div>
</div>
</div>
<div id="poping-notice" class="modal">
<div id="poping-notice-content">
<p>This webpage is free as in freedom software, it is offered to you with the hope it will be useful, but without any warranty,
you can find the source code at <a href="https://gitea.sergiotarxz.freemyip.com/sergiotarxz/Peertube-dl">my gitea</a> with docs to setup your own
webpage like this, this software is licensed under the AGPLv3 license which means you MUST convey the source code in a human readable form
if you distribute this software or use it as an service to users of service or distributees.<p>
<p>I hope that if you find a non supported url which should be supported, a bug, or a feature you would like this webpage to have you file an issue in
<a href="https://gitea.sergiotarxz.freemyip.com/sergiotarxz/Peertube-dl/issues">https://gitea.sergiotarxz.freemyip.com/sergiotarxz/Peertube-dl/issues</a>
to help this software improve since I find tracking users a pretty bad way to discover bugs and potential good features.</p>
<p>This webpage may load third party resources depending on the url you give to it which may put cookies in your browser, you are
encouraged to frecuently delete your browser cookies to avoid those third parties tracking you on internet, Firefox offers you an
option to delete cookies as soon as you close the browser which may be a good idea to enable in the orwellian internet of today.</p>
</div>
<div id="poping-notice-container-bar">
<a id="close-poping-notice" href="#">X</a>
</div>
</div>
</body>
</html>

View File

@ -1,13 +0,0 @@
{
"name": "default-theme",
"version": "1.0.0",
"private": true,
"license": "MIT",
"scripts": {
"build": "webpack"
},
"dependencies": {
"webpack": "^5.15.0",
"webpack-cli": "^4.3.1"
}
}

View File

@ -1,106 +0,0 @@
"use strict";
import { PopingNotice } from './view/poping_notice.js';
import { DownloadForm } from './view/download_form.js';
import { LoadingModal } from './view/loading_modal.js';
import { VideoContainer } from './view/video_container.js';
import { FormatSelector } from './view/format_selector.js';
class Application {
constructor() {
this.poping_notice = new PopingNotice();
this.download_form = new DownloadForm(this.onDownloadFormGot.bind(this));
this.loading_modal = new LoadingModal();
this.video_container = new VideoContainer();
this.format_selector = new FormatSelector();
}
init() {
this.popingNotice.setVisible(true);
}
onDownloadFormGot(url) {
this.dispatchURL(url);
}
dispatchURL(url, format) {
this.loadingModal.setVisible(true);
let error_str;
let success = this.queryAPI(url, format).then( (response) => {
if ( response.options !== undefined && response.options.list_formats !== undefined && response.options.list_formats ) {
if ( response.formats === undefined
|| response.formats.audio_formats === undefined
|| response.formats.video_formats === undefined ) {
throw 'Format object is not valid.';
}
this.formatSelector.prepareFormatSelector(
response.title, response.description,
response.formats.audio_formats, response.formats.video_formats,
( id ) => {
this.loadingModal.setVisible(true);
this.dispatchURL(url, id);
});
this.formatSelector.setVisible(true);
this.loadingModal.setVisible(false);
} else {
this.videoContainer.onCanPlay(this.onCanPlayVideoContainer.bind(this));
this.videoContainer.setURLVideo(response.url);
this.videoContainer.setFilename(response.filename);
}
}).catch( (error) => {
error_str = error.toString();
this.loadingModal.setVisible(false);
let input_url = document.createElement('a');
input_url.href = url;
input_url.innerText = url;
let issues_url = document.createElement('a');
issues_url.href = 'https://gitea.sergiotarxz.freemyip.com/sergiotarxz/Peertube-dl/issues';
issues_url.innerText = 'here';
this.popingNotice.setMessage( [ 'The url ', input_url, ' is not supported, the error was: ', error_str , ' if you think this is an error, report it ', issues_url, '.' ]);
this.popingNotice.setVisible(true);
});
}
onCanPlayVideoContainer() {
this.videoContainer.setVisible(true);
this.loadingModal.setVisible(false);
}
async queryAPI(url, format) {
let request = { url: url };
if (format !== undefined)
request.format = format;
const response = await fetch('/api', {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(request),
});
return response.json();
}
get formatSelector() {
return this.format_selector;
}
get videoContainer() {
return this.video_container;
}
get downloadForm() {
return this.download_form;
}
get popingNotice() {
return this.poping_notice;
}
get loadingModal() {
return this.loading_modal;
}
}
export { Application };

View File

@ -1,10 +0,0 @@
"use strict";
import { Application } from './application.js';
window.addEventListener('load', (event) => {
let application = new Application();
application.init();
});

View File

@ -1,35 +0,0 @@
"use strict";
class DownloadForm {
constructor(callback) {
this.query_selector = '#download-form';
this.callback = (event) => {
event.preventDefault();
callback(this.downloadFormUrl.value);
};
this.addEventListeners();
}
addEventListeners() {
this.downloadFormButton.addEventListener('click', this.callback);
this.element.addEventListener('submit', this.callback);
}
get downloadFormButton() {
return this.element.querySelector('#download-form-button');
}
get downloadFormUrl() {
return this.element.querySelector('#download-form-url');
}
get querySelector() {
return this.query_selector;
}
get element() {
return document.querySelector(this.querySelector);
}
}
export { DownloadForm };

View File

@ -1,120 +0,0 @@
"use strict";
class FormatSelector {
constructor() {
this.query_selector = '#modal-format-selector';
this.addEventListeners();
}
appendFormat(container, object, is_video, callback) {
let a = document.createElement('a');
let br = function() { return document.createElement('br') };
let inner_text = [];
let muted_video = false;
if ( is_video ) {
if ( object.audioSampleRate === undefined ) {
muted_video = true;
}
inner_text.push('Id: ' + object.id);
inner_text.push(br());
inner_text.push('Format: ' + object.mimeType);
inner_text.push(br());
inner_text.push('QualityLabel: ' + object.qualityLabel + "p");
inner_text.push(br());
inner_text.push('Bitrate: ' + object.bitrate);
inner_text.push(br());
inner_text.push( ( muted_video) ?
"No audio. " :
'AudioSampleRate: ' + object.audioSampleRate
);
} else {
inner_text.push('Id: ' + object.id + "\n");
inner_text.push(br());
inner_text.push('Format: ' + object.mimeType);
inner_text.push(br());
inner_text.push('AudioSampleRate: ' + object.audioSampleRate);
inner_text.push(br());
inner_text.push('Bitrate: ' + object.bitrate);
}
a.addEventListener( 'click', (event) => {
callback(object.id);
});
if (muted_video) {
let img_muted_video = document.createElement('img');
img_muted_video.classList.add('mute_img');
img_muted_video.classList.add('scale');
img_muted_video.src = 'img/audio_muted.svg';
inner_text.push(img_muted_video);
}
for (let text of inner_text) {
if (typeof text === "string"
|| text instanceof String) {
text = document.createTextNode(text);
a.appendChild(text);
} else if ( text instanceof Node) {
a.appendChild(text);
} else {
throw ('Text is not a instance of Node nor a String');
}
}
container.appendChild(a);
}
prepareFormatSelector(title, description, audio_formats, video_formats, callback) {
this.titleFormatSelector.innerText = title;
this.descriptionFormatSelector.innerText = description;
this.videoFormats.innerHTML = '';
this.audioFormats.innerHTML = '';
for ( let x of audio_formats) {
this.appendFormat(this.audioFormats, x, false, callback);
}
for ( let x of video_formats ) {
this.appendFormat(this.videoFormats, x, true, callback);
}
}
setVisible(option) {
if (option) {
this.element.classList.add('active');
} else {
this.element.classList.remove('active');
}
}
addEventListeners() {
this.closeFormatSelector.addEventListener('click', (event) => { this.setVisible(false); });
}
get videoFormats() {
return this.element.querySelector('.video-formats');
}
get audioFormats() {
return this.element.querySelector('.audio-formats');
}
get titleFormatSelector() {
return this.element.querySelector('h2');
}
get descriptionFormatSelector() {
return this.element.querySelector('p');
}
get closeFormatSelector() {
return this.element.querySelector('#close-modal-format-selector');
}
get element() {
return document.querySelector(this.querySelector);
}
get querySelector() {
return this.query_selector;
}
}
export { FormatSelector };

View File

@ -1,25 +0,0 @@
"use strict";
class LoadingModal {
constructor() {
this.query_selector = '#modal-loading';
}
setVisible(option) {
if (option) {
this.element.classList.add('active');
} else {
this.element.classList.remove('active');
}
}
get element() {
return document.querySelector(this.querySelector);
}
get querySelector() {
return this.query_selector;
}
}
export { LoadingModal };

View File

@ -1,56 +0,0 @@
"use strict";
class PopingNotice {
constructor() {
this.query_selector = '#poping-notice';
this.closePopingNotice.addEventListener('click', (event) => {
this.setVisible(false);
});
}
setVisible(option) {
if (option) {
this.element.classList.add('active');
} else {
this.element.classList.remove('active');
}
}
setMessage(message) {
if (!message instanceof Array)
throw 'Message is not instance of Array.';
let p = document.createElement('p');
for (let node of message) {
if (typeof node === "string"
|| node instanceof String) {
node = document.createTextNode(node);
p.appendChild(node);
} else if ( node instanceof Node) {
p.appendChild(node);
} else {
throw ('Node is not a instance of Node nor a String');
}
}
this.popingNoticeContent.innerHTML = '';
this.popingNoticeContent.appendChild(p);
}
get querySelector() {
return this.query_selector;
}
get element() {
return document.querySelector(this.querySelector);
}
get popingNoticeContent() {
return this.element.querySelector('#poping-notice-content');
}
get closePopingNotice() {
return this.element.querySelector('#close-poping-notice');
}
}
export { PopingNotice };

View File

@ -1,113 +0,0 @@
"use strict";
class VideoContainer {
constructor() {
this.query_selector = '#modal-video-container';
this.addEventListeners();
}
setVisible(option) {
if (option) {
this.element.classList.add('active');
} else {
this.video.pause();
this.element.classList.remove('active');
this.downloadVideoPrepare.classList.add('active');
this.downloadVideoLoading.classList.remove('active');
this.downloadVideo.classList.remove('active');
}
}
addEventListeners() {
this.downloadVideoPrepare.addEventListener('click', this.downloadPrepareHandler.bind(this));
this.closeAndResetVideoContainer.addEventListener('click', (event) => {
this.setVisible(false);
});
}
downloadPrepareHandler(event) {
this.downloadVideoPrepare.classList.remove('active');
this.downloadVideoLoading.classList.add('active');
this.generateBlobVideo(this.URLVideo).then( blob => {
this.downloadVideo.href = URL.createObjectURL(blob);
this.downloadVideo.download = this.filename;
this.downloadVideoLoading.classList.remove('active');
this.downloadVideo.classList.add('active');
});
}
async generateBlobVideo(url) {
const blob = await fetch(url, { mode: 'cors', })
.then(res => res.blob())
.catch( err => this.generateBlobVideoByProxy(url) );
return blob;
}
async generateBlobVideoByProxy(url) {
const blob = await fetch( '/proxy_to_get', {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({url: url}),
}
).then(res => res.blob());
return blob;
}
onCanPlay(callback) {
video.addEventListener('canplay', (event) => {
callback();
});
}
setURLVideo(url) {
this.url_video = url;
video.src = url;
}
setFilename(filename) {
this.filename = filename;
}
get closeAndResetVideoContainer() {
return this.element.querySelector('#close-and-reset-video-container');
}
get downloadVideo() {
return this.element.querySelector('#download-video');
}
get URLVideo() {
return this.url_video;
}
get closeAndResetVideoContainer() {
return this.element.querySelector('#close-and-reset-video-container');
}
get downloadVideoLoading() {
return this.element.querySelector('#download-video-loading');
}
get downloadVideoPrepare() {
return this.element.querySelector('#download-video-prepare');
}
get video() {
return this.element.querySelector('#video');
}
get element() {
return document.querySelector(this.querySelector);
}
get querySelector() {
return this.query_selector;
}
}
export { VideoContainer };

View File

@ -1,10 +0,0 @@
const path = require('path');
module.exports = {
entry: './src/index.js',
devtool: 'source-map',
output: {
filename: 'peertube-dl-web.js',
path: path.resolve(__dirname, 'dist/js'),
},
};

View File

@ -1,327 +0,0 @@
body {
height: 98vh;
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
background-color: #111827;
}
.application-container {
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
border-radius: 0.3rem;
background-color: #1f2937;
padding: 1.5rem 3rem 1.5rem 3rem;
}
.application-container form {
display: flex;
flex-flow: column;
justify-content: center;
width: 100%;
padding: 1rem;
}
.application-container h2 {
color: #ffffff;
font-weight: 400;
font-size: 1.6rem;
}
.application-container input {
padding: 1rem;
color: #ffffff;
background-color: #374151;
border-radius: 0.2rem;
border: 1px solid transparent;
}
.application-container button {
margin-top: 1rem;
color: #ffffff;
font-weight: bold;
background-color: #059669;
border-radius: 0.2rem;
border: 1px solid transparent;
padding: 0.5rem 1rem 0.5rem 1rem;
}
.application-container button:hover,.application-container button:focus {
background-color: #059;
}
.application-container button,
.application-container input {
font-size: 0.9rem;
}
.application-container.active {
display: flex;
}
#poping-notice-content a {
text-decoration: none;
color: #10b981;
}
#poping-notice-content a:hover,#poping-notice-content a:focus {
color: #0ae;
}
#poping-notice-container-bar {
display: flex;
justify-content: center;
}
#close-poping-notice {
background-color: #059669;
padding: 0.5rem 5rem 0.5rem 5rem;
border-radius: 0.3rem;
text-decoration: none;
font-weight: bolder;
color: white;
}
#close-poping-notice:hover,#close-poping-notice:focus {
background-color: #059;
}
.modal {
position: fixed;
display: none;
color: white;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#poping-notice {
padding: 3rem;
border-radius: 0.3rem;
background-color: #374151;
color: white;
overflow-y: scroll;
width: 94%;
max-height: 70%;
flex-direction: column;
scrollbar-width: none;
}
#modal-video-container,#modal-format-selector {
height: 100%;
width: 100%;
flex-flow: column;
align-items: center;
background-color: #111827;
}
#modal-format-selector h2 {
width: 100%;
}
#modal-format-selector p {
width: 100%;
}
.video-container-bar {
width: 99%;
height: 2rem;
padding-top: 1rem;
padding-bottom: 1rem;
display: flex;
justify-content: end;
margin-bottom: 1rem;
}
.video-container-bar a {
padding: 1rem;
margin-right: 1rem;
border-radius: 0.3rem;
display: flex;
align-items: center;
justify-content: center;
background-color: #dc2626;
}
.video-container-bar a:hover,.video-container-bar a:focus {
color: white;
background-color: grey;
}
#modal-video-container > #block {
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
}
#download-video-container {
padding: 2rem;
display: flex;
align-items: center;
justify-content: center;
}
.button-download {
display: none;
border-radius: 0.3rem;
background-color: #059669;
text-decoration: none;
color: white;
user-select: none;
cursor: pointer;
height: 30px;
padding: 0 2rem;
}
.button-download:hover,.button-download:focus {
background-color: #059;
}
.button-download embed {
height: 100%;
}
#download-video-loading.active {
height: 2rem;
}
#download-video-container .button-download.active {
display: flex;
align-items: center;
}
#modal-loading {
display: none;
}
.modal {
--tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
0 4px 6px -2px rgba(0, 0, 0, 0.05);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
overflow-y: scroll;
}
#poping-notice.active {
display: flex;
}
.modal.active {
display: flex;
scrollbar-width: none;
}
.modal.active.active::-webkit-scrollbar {
display: none;
}
#modal-loading {
display: none;
position: fixed;
top: 50%;
left: 50%;
height: 100%;
width: 100%;
transform: translate(-50%, -50%);
border: black 1px solid;
justify-content: right;
align-items: center;
}
#modal-loading.active {
display: flex;
}
#modal-loading embed {
width: 10%;
}
#video {
width: 100%;
max-width: 636px;
}
#video-container {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
.format-list > div {
display: grid;
grid-column-gap: 10px;
grid-row-gap: 10px;
grid-template-areas: "a";
grid-auto-columns: 99%;
margin: 1rem;
}
.format-list > div > a {
border: 1px solid black;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
padding-right: 5%;
padding-left: 5%;
text-decoration: none;
border-radius: 0.5rem;
background-color: #059669;
overflow-wrap: anywhere;
}
.format-list > div > a:focus,.format-list > div > a:hover {
background-color: #059;
}
.format-list > div > a:after {
padding-bottom: 100%;
display: block;
content: "";
}
.scale {
height: 1em;
width; 1em;
}
@media (min-width: 600px) {
.format-list > div {
grid-template-areas: "a a";
grid-auto-columns: 49.75%;
}
}
@media (min-width: 805px) {
.format-list > div {
grid-template-areas: "a a a";
grid-auto-columns: 33%;
}
}
@media (min-width: 812px) {
#poping-notice {
width: 40%;
}
}

View File

@ -1,18 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<g transform="matrix(.13811 0 0 .13811 4.964 5.832)" fill-opacity=".8">
<g transform="matrix(6.5167 0 0 6.5167-28.01-34.23)">
<g transform="matrix(1.17633 0 0 1.17633 1 1.589)" stroke-opacity=".85" fill="#000" stroke="none" fill-opacity="0" stroke-width=".73">
<path d="m11.243 12.993c-.192 0-.384-.073-.53-.22-.293-.293-.293-.768 0-1.061 2.047-2.047 2.047-5.378 0-7.425-.293-.293-.293-.768 0-1.061.293-.293.768-.293 1.061 0 1.275 1.275 1.977 2.97 1.977 4.773 0 1.803-.702 3.498-1.977 4.773-.146.146-.338.22-.53.22z"/>
<path d="m8.578 11.578c-.192 0-.384-.073-.53-.22-.293-.293-.293-.768 0-1.061 1.267-1.267 1.267-3.329 0-4.596-.293-.293-.293-.768 0-1.061.293-.293.768-.293 1.061 0 1.852 1.852 1.852 4.865 0 6.718-.146.146-.338.22-.53.22z"/>
</g>
<g transform="translate(16 4)">
<g fill="#f2f2f2" fill-opacity="0">
<path d="m-1.773 12.874c-.226 0-.452-.086-.623-.259-.345-.345-.345-.903 0-1.248 2.408-2.408 2.408-6.326 0-8.734-.345-.345-.345-.903 0-1.248.345-.345.903-.345 1.248 0 1.5 1.5 2.326 3.494 2.326 5.615 0 2.121-.826 4.115-2.326 5.615-.172.172-.398.259-.623.259z"/>
<path d="m-4.908 11.209c-.226 0-.452-.086-.623-.259-.345-.345-.345-.903 0-1.248 1.49-1.49 1.49-3.916 0-5.406-.345-.345-.345-.903 0-1.248.345-.345.903-.345 1.248 0 2.179 2.179 2.179 5.723 0 7.903-.172.172-.398.259-.623.259z"/>
</g>
<path d="m-7.353 15.235c-.153 0-.303-.06-.416-.172l-4.534-4.534h-2.109c-.325 0-.588-.263-.588-.588v-5.882c0-.325.263-.588.588-.588h2.109l4.534-4.534c.168-.168.421-.219.641-.127.22.092.363.306.363.543v15.292c0 .238-.144.453-.363.543-.073.031-.149.045-.225.045" fill="#4d4d4d" fill-opacity="1"/>
</g>
</g>
<path d="m14.75 9.674v1.326h-1.326l-1.674-1.674-1.674 1.674h-1.326v-1.326l1.674-1.674-1.674-1.674v-1.326h1.326l1.674 1.674 1.674-1.674h1.326v1.326l-1.674 1.674 1.674 1.674" fill="#4d4d4d" fill-opacity="1" transform="matrix(8.22579 0 0 8.22579-32.543-28.351)"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -1,76 +0,0 @@
<html>
<head>
<script src="js/peertube-dl-web.js?v=1"></script>
<link rel="stylesheet" href="css/index.css?v=4"></script>
<link rel="icon" type="image/png" href="img/peertube-dl-logo.png"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Peertube-dl Web</title>
<meta name="description" content="Webpage intended to download videos from various webpages for fair use."/>
<meta property="og:type" content="website" />
<meta property="og:title" content="Peertube-dl Web"/>
<meta property="og:site_name" content="Peertube-dl"/>
<meta property="og:description" content="Webpage intended to download videos from various webpages for fair use."/>
<meta property="og:image" content="./img/peertube-dl-logo-fullsize.png"/>
</head>
<body>
<div class="application-container">
<h2>Peertube-dl Web Application</h2>
<form id="download-form">
<input class="block" type="text" id="download-form-url" placeholder="Introduce the url you want to download."/>
<button class="block" id="download-form-button" >Fetch from api</button>
</form>
</div>
<div id="modal-format-selector" class="modal">
<div class="video-container-bar">
<a id="close-modal-format-selector">x</a>
</div>
<h2>Example video</h2>
<p>Example description</p>
<div class="format-list">
<h3>Video Formats.</h3>
<div class="video-formats">
</div>
<h3>Audio Formats.</h3>
<div class="audio-formats">
</div>
</div>
</div>
<div id="modal-loading">
<embed src="img/spinner.svg"/>
</div>
<div id="modal-video-container" class="modal">
<div class="video-container-bar">
<a id="close-and-reset-video-container">x</a>
</div>
<div id="video-container">
<div class="block">
<video id="video" controls="controls"></video>
</div>
<div id="download-video-container" class="block">
<a id="download-video-prepare" class="button-download active">Prepare download</a>
<a id="download-video-loading" class="button-download"><embed src="img/spinner.svg"/></a>
<a id="download-video" class="button-download">Download</a>
</div>
</div>
</div>
<div id="poping-notice" class="modal">
<div id="poping-notice-content">
<p>This webpage is free as in freedom software, it is offered to you with the hope it will be useful, but without any warranty,
you can find the source code at <a href="https://gitea.sergiotarxz.freemyip.com/sergiotarxz/Peertube-dl">my gitea</a> with docs to setup your own
webpage like this, this software is licensed under the AGPLv3 license which means you MUST convey the source code in a human readable form
if you distribute this software or use it as an service to users of service or distributees.<p>
<p>I hope that if you find a non supported url which should be supported, a bug, or a feature you would like this webpage to have you file an issue in
<a href="https://gitea.sergiotarxz.freemyip.com/sergiotarxz/Peertube-dl/issues">https://gitea.sergiotarxz.freemyip.com/sergiotarxz/Peertube-dl/issues</a>
to help this software improve since I find tracking users a pretty bad way to discover bugs and potential good features.</p>
<p>This webpage may load third party resources depending on the url you give to it which may put cookies in your browser, you are
encouraged to frecuently delete your browser cookies to avoid those third parties tracking you on internet, Firefox offers you an
option to delete cookies as soon as you close the browser which may be a good idea to enable in the orwellian internet of today.</p>
</div>
<div id="poping-notice-container-bar">
<a id="close-poping-notice" href="#">X</a>
</div>
</div>
</body>
</html>

View File

@ -1,13 +0,0 @@
{
"name": "default-theme",
"version": "1.0.0",
"private": true,
"license": "MIT",
"scripts": {
"build": "webpack"
},
"dependencies": {
"webpack": "^5.15.0",
"webpack-cli": "^4.3.1"
}
}

View File

@ -1,106 +0,0 @@
"use strict";
import { PopingNotice } from './view/poping_notice.js';
import { DownloadForm } from './view/download_form.js';
import { LoadingModal } from './view/loading_modal.js';
import { VideoContainer } from './view/video_container.js';
import { FormatSelector } from './view/format_selector.js';
class Application {
constructor() {
this.poping_notice = new PopingNotice();
this.download_form = new DownloadForm(this.onDownloadFormGot.bind(this));
this.loading_modal = new LoadingModal();
this.video_container = new VideoContainer();
this.format_selector = new FormatSelector();
}
init() {
this.popingNotice.setVisible(true);
}
onDownloadFormGot(url) {
this.dispatchURL(url);
}
dispatchURL(url, format) {
this.loadingModal.setVisible(true);
let error_str;
let success = this.queryAPI(url, format).then( (response) => {
if ( response.options !== undefined && response.options.list_formats !== undefined && response.options.list_formats ) {
if ( response.formats === undefined
|| response.formats.audio_formats === undefined
|| response.formats.video_formats === undefined ) {
throw 'Format object is not valid.';
}
this.formatSelector.prepareFormatSelector(
response.title, response.description,
response.formats.audio_formats, response.formats.video_formats,
( id ) => {
this.loadingModal.setVisible(true);
this.dispatchURL(url, id);
});
this.formatSelector.setVisible(true);
this.loadingModal.setVisible(false);
} else {
this.videoContainer.onCanPlay(this.onCanPlayVideoContainer.bind(this));
this.videoContainer.setURLVideo(response.url);
this.videoContainer.setFilename(response.filename);
}
}).catch( (error) => {
error_str = error.toString();
this.loadingModal.setVisible(false);
let input_url = document.createElement('a');
input_url.href = url;
input_url.innerText = url;
let issues_url = document.createElement('a');
issues_url.href = 'https://gitea.sergiotarxz.freemyip.com/sergiotarxz/Peertube-dl/issues';
issues_url.innerText = 'here';
this.popingNotice.setMessage( [ 'The url ', input_url, ' is not supported, the error was: ', error_str , ' if you think this is an error, report it ', issues_url, '.' ]);
this.popingNotice.setVisible(true);
});
}
onCanPlayVideoContainer() {
this.videoContainer.setVisible(true);
this.loadingModal.setVisible(false);
}
async queryAPI(url, format) {
let request = { url: url };
if (format !== undefined)
request.format = format;
const response = await fetch('/api', {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(request),
});
return response.json();
}
get formatSelector() {
return this.format_selector;
}
get videoContainer() {
return this.video_container;
}
get downloadForm() {
return this.download_form;
}
get popingNotice() {
return this.poping_notice;
}
get loadingModal() {
return this.loading_modal;
}
}
export { Application };

View File

@ -1,10 +0,0 @@
"use strict";
import { Application } from './application.js';
window.addEventListener('load', (event) => {
let application = new Application();
application.init();
});

View File

@ -1,35 +0,0 @@
"use strict";
class DownloadForm {
constructor(callback) {
this.query_selector = '#download-form';
this.callback = (event) => {
event.preventDefault();
callback(this.downloadFormUrl.value);
};
this.addEventListeners();
}
addEventListeners() {
this.downloadFormButton.addEventListener('click', this.callback);
this.element.addEventListener('submit', this.callback);
}
get downloadFormButton() {
return this.element.querySelector('#download-form-button');
}
get downloadFormUrl() {
return this.element.querySelector('#download-form-url');
}
get querySelector() {
return this.query_selector;
}
get element() {
return document.querySelector(this.querySelector);
}
}
export { DownloadForm };

View File

@ -1,120 +0,0 @@
"use strict";
class FormatSelector {
constructor() {
this.query_selector = '#modal-format-selector';
this.addEventListeners();
}
appendFormat(container, object, is_video, callback) {
let a = document.createElement('a');
let br = function() { return document.createElement('br') };
let inner_text = [];
let muted_video = false;
if ( is_video ) {
if ( object.audioSampleRate === undefined ) {
muted_video = true;
}
inner_text.push('Id: ' + object.id);
inner_text.push(br());
inner_text.push('Format: ' + object.mimeType);
inner_text.push(br());
inner_text.push('QualityLabel: ' + object.qualityLabel + "p");
inner_text.push(br());
inner_text.push('Bitrate: ' + object.bitrate);
inner_text.push(br());
inner_text.push( ( muted_video) ?
"No audio. " :
'AudioSampleRate: ' + object.audioSampleRate
);
} else {
inner_text.push('Id: ' + object.id + "\n");
inner_text.push(br());
inner_text.push('Format: ' + object.mimeType);
inner_text.push(br());
inner_text.push('AudioSampleRate: ' + object.audioSampleRate);
inner_text.push(br());
inner_text.push('Bitrate: ' + object.bitrate);
}
a.addEventListener( 'click', (event) => {
callback(object.id);
});
if (muted_video) {
let img_muted_video = document.createElement('img');
img_muted_video.classList.add('mute_img');
img_muted_video.classList.add('scale');
img_muted_video.src = 'img/audio_muted.svg';
inner_text.push(img_muted_video);
}
for (let text of inner_text) {
if (typeof text === "string"
|| text instanceof String) {
text = document.createTextNode(text);
a.appendChild(text);
} else if ( text instanceof Node) {
a.appendChild(text);
} else {
throw ('Text is not a instance of Node nor a String');
}
}
container.appendChild(a);
}
prepareFormatSelector(title, description, audio_formats, video_formats, callback) {
this.titleFormatSelector.innerText = title;
this.descriptionFormatSelector.innerText = description;
this.videoFormats.innerHTML = '';
this.audioFormats.innerHTML = '';
for ( let x of audio_formats) {
this.appendFormat(this.audioFormats, x, false, callback);
}
for ( let x of video_formats ) {
this.appendFormat(this.videoFormats, x, true, callback);
}
}
setVisible(option) {
if (option) {
this.element.classList.add('active');
} else {
this.element.classList.remove('active');
}
}
addEventListeners() {
this.closeFormatSelector.addEventListener('click', (event) => { this.setVisible(false); });
}
get videoFormats() {
return this.element.querySelector('.video-formats');
}
get audioFormats() {
return this.element.querySelector('.audio-formats');
}
get titleFormatSelector() {
return this.element.querySelector('h2');
}
get descriptionFormatSelector() {
return this.element.querySelector('p');
}
get closeFormatSelector() {
return this.element.querySelector('#close-modal-format-selector');
}
get element() {
return document.querySelector(this.querySelector);
}
get querySelector() {
return this.query_selector;
}
}
export { FormatSelector };

View File

@ -1,25 +0,0 @@
"use strict";
class LoadingModal {
constructor() {
this.query_selector = '#modal-loading';
}
setVisible(option) {
if (option) {
this.element.classList.add('active');
} else {
this.element.classList.remove('active');
}
}
get element() {
return document.querySelector(this.querySelector);
}
get querySelector() {
return this.query_selector;
}
}
export { LoadingModal };

View File

@ -1,56 +0,0 @@
"use strict";
class PopingNotice {
constructor() {
this.query_selector = '#poping-notice';
this.closePopingNotice.addEventListener('click', (event) => {
this.setVisible(false);
});
}
setVisible(option) {
if (option) {
this.element.classList.add('active');
} else {
this.element.classList.remove('active');
}
}
setMessage(message) {
if (!message instanceof Array)
throw 'Message is not instance of Array.';
let p = document.createElement('p');
for (let node of message) {
if (typeof node === "string"
|| node instanceof String) {
node = document.createTextNode(node);
p.appendChild(node);
} else if ( node instanceof Node) {
p.appendChild(node);
} else {
throw ('Node is not a instance of Node nor a String');
}
}
this.popingNoticeContent.innerHTML = '';
this.popingNoticeContent.appendChild(p);
}
get querySelector() {
return this.query_selector;
}
get element() {
return document.querySelector(this.querySelector);
}
get popingNoticeContent() {
return this.element.querySelector('#poping-notice-content');
}
get closePopingNotice() {
return this.element.querySelector('#close-poping-notice');
}
}
export { PopingNotice };

View File

@ -1,113 +0,0 @@
"use strict";
class VideoContainer {
constructor() {
this.query_selector = '#modal-video-container';
this.addEventListeners();
}
setVisible(option) {
if (option) {
this.element.classList.add('active');
} else {
this.video.pause();
this.element.classList.remove('active');
this.downloadVideoPrepare.classList.add('active');
this.downloadVideoLoading.classList.remove('active');
this.downloadVideo.classList.remove('active');
}
}
addEventListeners() {
this.downloadVideoPrepare.addEventListener('click', this.downloadPrepareHandler.bind(this));
this.closeAndResetVideoContainer.addEventListener('click', (event) => {
this.setVisible(false);
});
}
downloadPrepareHandler(event) {
this.downloadVideoPrepare.classList.remove('active');
this.downloadVideoLoading.classList.add('active');
this.generateBlobVideo(this.URLVideo).then( blob => {
this.downloadVideo.href = URL.createObjectURL(blob);
this.downloadVideo.download = this.filename;
this.downloadVideoLoading.classList.remove('active');
this.downloadVideo.classList.add('active');
});
}
async generateBlobVideo(url) {
const blob = await fetch(url, { mode: 'cors', })
.then(res => res.blob())
.catch( err => this.generateBlobVideoByProxy(url) );
return blob;
}
async generateBlobVideoByProxy(url) {
const blob = await fetch( '/proxy_to_get', {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({url: url}),
}
).then(res => res.blob());
return blob;
}
onCanPlay(callback) {
video.addEventListener('canplay', (event) => {
callback();
});
}
setURLVideo(url) {
this.url_video = url;
video.src = url;
}
setFilename(filename) {
this.filename = filename;
}
get closeAndResetVideoContainer() {
return this.element.querySelector('#close-and-reset-video-container');
}
get downloadVideo() {
return this.element.querySelector('#download-video');
}
get URLVideo() {
return this.url_video;
}
get closeAndResetVideoContainer() {
return this.element.querySelector('#close-and-reset-video-container');
}
get downloadVideoLoading() {
return this.element.querySelector('#download-video-loading');
}
get downloadVideoPrepare() {
return this.element.querySelector('#download-video-prepare');
}
get video() {
return this.element.querySelector('#video');
}
get element() {
return document.querySelector(this.querySelector);
}
get querySelector() {
return this.query_selector;
}
}
export { VideoContainer };

View File

@ -1,10 +0,0 @@
const path = require('path');
module.exports = {
entry: './src/index.js',
devtool: 'source-map',
output: {
filename: 'peertube-dl-web.js',
path: path.resolve(__dirname, 'dist/js'),
},
};