feature/Add_posibility_of_selecting_diferent_formats #18

Merged
sergiotarxz merged 3 commits from feature/Add_posibility_of_selecting_diferent_formats into master 2021-01-13 09:24:51 +01:00
6 changed files with 253 additions and 54 deletions

View File

@ -22,11 +22,12 @@ post '/api' => sub {
status => 400 status => 400
) )
); );
my $format = $c->req->json->{format};
my $render_returned; my $render_returned;
eval { eval {
$render_returned = $c->render( $render_returned = $c->render(
json => Peertube::DL::URLHandler::getDownloadDataFromURL( json => Peertube::DL::URLHandler::getDownloadDataFromURL(
$url, { format => 18 } $url, { ( ( defined $format ) ? ( format => $format ) : () ) },
) )
); );
}; };

View File

@ -43,7 +43,6 @@ sub youtube {
: () : ()
); );
my $mime_type = $format->{mimeType} =~ s/;.*$//r; my $mime_type = $format->{mimeType} =~ s/;.*$//r;
say $mime_type;
my $extension = $mime_type =~ s/^.*?\///r =~ s/;.*$//r; my $extension = $mime_type =~ s/^.*?\///r =~ s/;.*$//r;
if ( defined $format->{url} ) { if ( defined $format->{url} ) {
return { return {
@ -65,8 +64,6 @@ sub youtube {
$url_data $url_data
}; };
say Data::Dumper::Dumper $url_data;
my ($player_url) = my ($player_url) =
$response->decoded_content =~ m/"jsUrl"\s*:\s*("[^"]+")/; $response->decoded_content =~ m/"jsUrl"\s*:\s*("[^"]+")/;
$player_url = JSON::from_json( $player_url, { allow_nonref => 1 } ); $player_url = JSON::from_json( $player_url, { allow_nonref => 1 } );
@ -101,22 +98,36 @@ sub youtube {
my $signature = my $signature =
$js->callJSFunction( $function_name_regen_sig, $url_data->{s} ); $js->callJSFunction( $function_name_regen_sig, $url_data->{s} );
my $url = $url_data->{url} . "&sig=${signature}"; my $url = $url_data->{url} . "&sig=${signature}";
say Data::Dumper::Dumper $format;
say $url; say $url;
return { return {
url => $url, url => $url,
filename => $microformat->{title}{simpleText} . '.' . $extension, filename => $microformat->{title}{simpleText} . '.' . $extension,
}; };
} else { } else {
return { my @formats = map {
options => { list_formats => 1 },
title => $microformat->{title}{simpleText},
description => $microformat->{description}{simpleText},
formats => [
map {
{ {
id => $_->{itag}, id => $_->{itag},
mimeType => $_->{mimeType} 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} } scalar @{ $ytInitialPlayerResponse->{adaptiveFormats} }
@ -125,8 +136,32 @@ sub youtube {
scalar @{ $ytInitialPlayerResponse->{formats} } scalar @{ $ytInitialPlayerResponse->{formats} }
? @{ $ytInitialPlayerResponse->{formats} } ? @{ $ytInitialPlayerResponse->{formats} }
: () : ()
) );
] my $video_formats = [
sort {
$b->{qualityLabel} <=> $a->{qualityLabel}
|| $b->{bitrate} <=> $a->{bitrate}
} 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,
},
}; };
} }
} }

View File

@ -66,7 +66,7 @@ sub getDownloadDataFromURL {
die "No title." unless defined $download_data->{title}; die "No title." unless defined $download_data->{title};
die "No description." unless defined $download_data->{description}; die "No description." unless defined $download_data->{description};
die "No formats available." unless defined $download_data->{formats}; die "No formats available." unless defined $download_data->{formats};
die "Formats is not an arrayref." unless ref $download_data->{formats} eq 'ARRAY'; die "Formats is not a hash." unless ref $download_data->{formats} eq 'HASH';
say "The video title is $download_data->{title}."; say "The video title is $download_data->{title}.";
say "The video description is $download_data->{description}."; say "The video description is $download_data->{description}.";
say "The available formats are: @{[Data::Dumper::Dumper $download_data->{formats}]}."; say "The available formats are: @{[Data::Dumper::Dumper $download_data->{formats}]}.";

View File

@ -135,7 +135,7 @@ h2 {
width: 10%; width: 10%;
} }
.poping-notice { #poping-notice {
display: none; display: none;
position: fixed; position: fixed;
top: 50%; top: 50%;
@ -149,17 +149,18 @@ h2 {
max-height: 95%; max-height: 95%;
overflow-y: scroll; overflow-y: scroll;
} }
.poping-notice.active {
#poping-notice.active {
display: block; display: block;
} }
.poping-notice-container-bar { #poping-notice-container-bar {
display: flex; display: flex;
justify-content: center; justify-content: center;
font-size: 5rem; font-size: 5rem;
} }
.close-poping-notice { #close-poping-notice {
width: 150px; width: 150px;
height: 150px; height: 150px;
align-items: center; align-items: center;
@ -172,14 +173,97 @@ h2 {
text-decoration: none; text-decoration: none;
} }
.close-poping-notice:hover,.close-poping-notice:focus { #close-poping-notice:hover,#close-poping-notice:focus {
background: black; background: black;
color: white; color: white;
} }
#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;
}
#modal-format-selector.active {
display: flex;
}
#modal-format-selector > h2 {
text-align: center;
}
#modal-format-selector > p {
margin-left: 2rem;
}
#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;
}
.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) { @media (min-width: 668px) {
.poping-notice { #poping-notice {
width: 629px; width: 629px;
} }
} }

View File

@ -12,12 +12,27 @@
<button class="block" id="download-form-button" >Fetch from api</button> <button class="block" id="download-form-button" >Fetch from api</button>
</form> </form>
</div> </div>
<div id="modal-format-selector">
<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"> <div id="modal-loading">
<embed src="img/spinner.svg"/> <embed src="img/spinner.svg"/>
</div> </div>
<div id="modal-video-container"> <div id="modal-video-container">
<div class="video-container-bar"> <div class="video-container-bar">
<a id="close-and-reset-video-container">X</a> <a id="close-and-reset-video-container">x</a>
</div> </div>
<div id="video-container"> <div id="video-container">
<div class="block"> <div class="block">
@ -30,8 +45,8 @@
</div> </div>
</div> </div>
</div> </div>
<div class="poping-notice"> <div id="poping-notice">
<div class="poping-notice-content"> <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, <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 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 webpage like this, this software is licensed under the AGPLv3 license which means you MUST convey the source code in a human readable form
@ -45,8 +60,8 @@
encouraged to frecuently delete your browser cookies to avoid those third parties tracking you on internet, Firefox offers you an 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> 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>
<div class="poping-notice-container-bar"> <div id="poping-notice-container-bar">
<a class="close-poping-notice" href="#">X</a> <a id="close-poping-notice" href="#">X</a>
</div> </div>
</div> </div>
</body> </body>

View File

@ -15,11 +15,59 @@ let url;
let popingNotice; let popingNotice;
let popingNoticeContent; let popingNoticeContent;
let closePopingNotice; let closePopingNotice;
let titleModalFormatSelector;
let descriptionModalFormatSelector;
let modalFormatSelector;
let videoFormats;
let audioFormats;
let closeModalFormatSelector;
function downloadFormButtonHandler(event) { function downloadFormButtonHandler(event) {
event.preventDefault(); event.preventDefault();
modalLoading.classList.add('active'); modalLoading.classList.add('active');
getRealURL(downloadFormUrl.value).then( (response) => { askForURL(downloadFormUrl.value);
}
function askForURL(url, format) {
getRealURL(url, format).then( (response) => {
if ( response.options !== undefined && response.options.list_formats !== undefined && response.options.list_formats ) {
titleModalFormatSelector.innerText = response.title;
descriptionModalFormatSelector.innerText = response.description;
videoFormats.innerHTML = '';
audioFormats.innerHTML = '';
for ( let x of response.formats.audio_formats) {
let a = document.createElement('a');
a.innerText = 'Id: ' + x.id + "\n"
+ 'Format: ' + x.mimeType + "\n"
+ 'AudioSampleRate: ' + x.audioSampleRate + "\n"
+ 'Bitrate: ' + x.bitrate + ".\n";
a.addEventListener( 'click', (event) => {
modalLoading.classList.add('active');
askForURL(url, x.id );
});
audioFormats.appendChild(a);
modalLoading.classList.remove('active');
modalFormatSelector.classList.add('active');
}
for ( let x of response.formats.video_formats ) {
let a = document.createElement('a');
a.innerText = 'Id: ' + x.id + "\n"
+ 'Format: ' + x.mimeType + "\n"
+ 'QualityLabel: ' + x.qualityLabel + "p\n"
+ 'Bitrate: ' + x.bitrate + "\n"
+ (
( x.audioSampleRate !== undefined ) ?
'AudioSampleRate: ' + x.audioSampleRate + ".\n" :
"No audio."
);
a.addEventListener( 'click', (event) => {
modalLoading.classList.add('active');
askForURL(url, x.id );
});
videoFormats.appendChild(a);
modalLoading.classList.remove('active');
modalFormatSelector.classList.add('active');
}
} else {
video.src = response.url; video.src = response.url;
filename = response.filename; filename = response.filename;
url = response.url; url = response.url;
@ -27,14 +75,17 @@ function downloadFormButtonHandler(event) {
modalLoading.classList.remove('active'); modalLoading.classList.remove('active');
modalVideoContainer.style.display = 'block'; modalVideoContainer.style.display = 'block';
}); });
}
return true;
}).catch ( (error) => { }).catch ( (error) => {
console.log(error);
modalLoading.classList.remove('active'); modalLoading.classList.remove('active');
popingNoticeContent.innerHTML = ''; popingNoticeContent.innerHTML = '';
let p = document.createElement('p'); let p = document.createElement('p');
p.appendChild(document.createTextNode('The url ')); p.appendChild(document.createTextNode('The url '));
let input_url = document.createElement('a'); let input_url = document.createElement('a');
input_url.href = downloadFormUrl.value; input_url.href = url;
input_url.innerText = downloadFormUrl.value; input_url.innerText = url;
p.appendChild(input_url) p.appendChild(input_url)
p.appendChild(document.createTextNode(' is not supported, if you think this is an error, report it ')); p.appendChild(document.createTextNode(' is not supported, if you think this is an error, report it '));
let issues_url = 'https://gitea.sergiotarxz.freemyip.com/sergiotarxz/Peertube-dl/issues'; let issues_url = 'https://gitea.sergiotarxz.freemyip.com/sergiotarxz/Peertube-dl/issues';
@ -45,6 +96,7 @@ function downloadFormButtonHandler(event) {
p.appendChild(document.createTextNode('.')); p.appendChild(document.createTextNode('.'));
popingNoticeContent.appendChild(p); popingNoticeContent.appendChild(p);
popingNotice.classList.add('active'); popingNotice.classList.add('active');
return false;
}); });
} }
@ -86,7 +138,10 @@ async function generateBlobVideoByProxy(url) {
return blob; return blob;
} }
async function getRealURL(url) { async function getRealURL(url, format) {
let request = { url: url };
if (format !== undefined)
request.format = format;
const response = await fetch('/api', { const response = await fetch('/api', {
method: 'POST', method: 'POST',
mode: 'cors', mode: 'cors',
@ -94,7 +149,7 @@ async function getRealURL(url) {
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({url: url}), body: JSON.stringify(request),
}); });
return response.json(); return response.json();
} }
@ -118,14 +173,23 @@ window.addEventListener('load', (event) => {
downloadVideoPrepare = document.querySelector('#download-video-prepare'); downloadVideoPrepare = document.querySelector('#download-video-prepare');
closeAndResetVideoContainer = document.querySelector('#close-and-reset-video-container'); closeAndResetVideoContainer = document.querySelector('#close-and-reset-video-container');
modalLoading = document.querySelector('#modal-loading'); modalLoading = document.querySelector('#modal-loading');
popingNotice = document.querySelector('.poping-notice'); popingNotice = document.querySelector('#poping-notice');
popingNoticeContent = document.querySelector('.poping-notice-content'); popingNoticeContent = document.querySelector('#poping-notice-content');
closePopingNotice = document.querySelector('.close-poping-notice'); closePopingNotice = document.querySelector('#close-poping-notice');
titleModalFormatSelector = document.querySelector('#modal-format-selector h2');
descriptionModalFormatSelector = document.querySelector('#modal-format-selector p');
modalFormatSelector = document.querySelector('#modal-format-selector');
videoFormats = document.querySelector('#modal-format-selector .video-formats');
audioFormats = document.querySelector('#modal-format-selector .audio-formats');
closeModalFormatSelector = document.querySelector('#close-modal-format-selector');
downloadFormButton.addEventListener('click', downloadFormButtonHandler); downloadFormButton.addEventListener('click', downloadFormButtonHandler);
downloadVideoPrepare.addEventListener('click', downloadVideoPrepareHandler); downloadVideoPrepare.addEventListener('click', downloadVideoPrepareHandler);
downloadForm.addEventListener('submit', downloadFormHandler); downloadForm.addEventListener('submit', downloadFormHandler);
closeAndResetVideoContainer.addEventListener('click', closeAndResetVideoContainerHandler); closeAndResetVideoContainer.addEventListener('click', closeAndResetVideoContainerHandler);
closeModalFormatSelector.addEventListener('click', (event) => {
modalFormatSelector.classList.remove('active');
});
closePopingNotice.addEventListener('click', (event) => { closePopingNotice.addEventListener('click', (event) => {
popingNotice.classList.remove('active'); popingNotice.classList.remove('active');
}); });