diff --git a/bin/peertube-dl-web b/bin/peertube-dl-web index 1bedec1..9ae5ba6 100755 --- a/bin/peertube-dl-web +++ b/bin/peertube-dl-web @@ -22,11 +22,12 @@ post '/api' => sub { status => 400 ) ); + my $format = $c->req->json->{format}; my $render_returned; eval { $render_returned = $c->render( json => Peertube::DL::URLHandler::getDownloadDataFromURL( - $url, { format => 18 } + $url, { ( ( defined $format ) ? ( format => $format ) : () ) }, ) ); }; diff --git a/lib/Peertube/DL/Downloaders.pm b/lib/Peertube/DL/Downloaders.pm index e4bee57..6011805 100644 --- a/lib/Peertube/DL/Downloaders.pm +++ b/lib/Peertube/DL/Downloaders.pm @@ -43,7 +43,6 @@ sub youtube { : () ); my $mime_type = $format->{mimeType} =~ s/;.*$//r; - say $mime_type; my $extension = $mime_type =~ s/^.*?\///r =~ s/;.*$//r; if ( defined $format->{url} ) { return { @@ -65,8 +64,6 @@ sub youtube { $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 } ); @@ -101,32 +98,70 @@ sub youtube { my $signature = $js->callJSFunction( $function_name_regen_sig, $url_data->{s} ); my $url = $url_data->{url} . "&sig=${signature}"; - say Data::Dumper::Dumper $format; say $url; return { url => $url, filename => $microformat->{title}{simpleText} . '.' . $extension, }; } else { + my @formats = map { + { + id => $_->{itag}, + mimeType => $_->{mimeType}, + ( + ( defined $_->{averageBitrate} && defined $_->{bitrate} ) + ? ( bitrate => $_->{averageBitrate} // $_->{bitrate} ) + : () + ), + ( + ( defined $_->{qualityLabel} ) + ? ( qualityLabel => $_->{qualityLabel} =~ s/p$//r ) + : () + ), + ( + ( defined $_->{audioSampleRate} ) + ? ( audioSampleRate => $_->{audioSampleRate} ) + : () + ), + ( + ( defined $_->{quality} ) + ? ( quality => $_->{quality} ) + : () + ), + } + } ( + scalar @{ $ytInitialPlayerResponse->{adaptiveFormats} } + ? @{ $ytInitialPlayerResponse->{adaptiveFormats} } + : (), + scalar @{ $ytInitialPlayerResponse->{formats} } + ? @{ $ytInitialPlayerResponse->{formats} } + : () + ); + my $video_formats = [ + sort { + $b->{qualityLabel} <=> $a->{qualityLabel} + || $b->{bitrate} <=> $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 => [ - map { - { - id => $_->{itag}, - mimeType => $_->{mimeType} - } - } ( - scalar @{ $ytInitialPlayerResponse->{adaptiveFormats} } - ? @{ $ytInitialPlayerResponse->{adaptiveFormats} } - : (), - scalar @{ $ytInitialPlayerResponse->{formats} } - ? @{ $ytInitialPlayerResponse->{formats} } - : () - ) - ] + formats => { + video_formats => $video_formats, + audio_formats => $audio_formats, + }, + }; } } diff --git a/lib/Peertube/DL/URLHandler.pm b/lib/Peertube/DL/URLHandler.pm index 115e48b..3715bf9 100644 --- a/lib/Peertube/DL/URLHandler.pm +++ b/lib/Peertube/DL/URLHandler.pm @@ -66,7 +66,7 @@ sub getDownloadDataFromURL { 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'; + 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}]}."; diff --git a/lib/Peertube/DL/public/css/index.css b/lib/Peertube/DL/public/css/index.css index ca46a24..fa83ef8 100644 --- a/lib/Peertube/DL/public/css/index.css +++ b/lib/Peertube/DL/public/css/index.css @@ -135,7 +135,7 @@ h2 { width: 10%; } -.poping-notice { +#poping-notice { display: none; position: fixed; top: 50%; @@ -149,17 +149,18 @@ h2 { max-height: 95%; overflow-y: scroll; } -.poping-notice.active { + +#poping-notice.active { display: block; } -.poping-notice-container-bar { +#poping-notice-container-bar { display: flex; justify-content: center; font-size: 5rem; } -.close-poping-notice { +#close-poping-notice { width: 150px; height: 150px; align-items: center; @@ -172,14 +173,97 @@ h2 { text-decoration: none; } -.close-poping-notice:hover,.close-poping-notice:focus { +#close-poping-notice:hover,#close-poping-notice:focus { background: black; 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) { - .poping-notice { + #poping-notice { width: 629px; } } - diff --git a/lib/Peertube/DL/public/index.html b/lib/Peertube/DL/public/index.html index 729e86b..4eac01f 100644 --- a/lib/Peertube/DL/public/index.html +++ b/lib/Peertube/DL/public/index.html @@ -12,12 +12,27 @@ + -
-
+
+

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 my gitea 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.

-

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 - https://gitea.sergiotarxz.freemyip.com/sergiotarxz/Peertube-dl/issues - to help this software improve since I find tracking users a pretty bad way to discover bugs and potential good features.

+

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 + https://gitea.sergiotarxz.freemyip.com/sergiotarxz/Peertube-dl/issues + to help this software improve since I find tracking users a pretty bad way to discover bugs and potential good features.

-

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.

+

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.

-
- X +
+ X
diff --git a/lib/Peertube/DL/public/js/peertube-dl-web.js b/lib/Peertube/DL/public/js/peertube-dl-web.js index f6a6d31..2b85275 100644 --- a/lib/Peertube/DL/public/js/peertube-dl-web.js +++ b/lib/Peertube/DL/public/js/peertube-dl-web.js @@ -15,26 +15,77 @@ let url; let popingNotice; let popingNoticeContent; let closePopingNotice; +let titleModalFormatSelector; +let descriptionModalFormatSelector; +let modalFormatSelector; +let videoFormats; +let audioFormats; +let closeModalFormatSelector; function downloadFormButtonHandler(event) { event.preventDefault(); modalLoading.classList.add('active'); - getRealURL(downloadFormUrl.value).then( (response) => { - video.src = response.url; - filename = response.filename; - url = response.url; - video.addEventListener('canplay', (event) => { - modalLoading.classList.remove('active'); - modalVideoContainer.style.display = 'block'; - }); + 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; + filename = response.filename; + url = response.url; + video.addEventListener('canplay', (event) => { + modalLoading.classList.remove('active'); + modalVideoContainer.style.display = 'block'; + }); + } + return true; }).catch ( (error) => { + console.log(error); modalLoading.classList.remove('active'); popingNoticeContent.innerHTML = ''; let p = document.createElement('p'); p.appendChild(document.createTextNode('The url ')); let input_url = document.createElement('a'); - input_url.href = downloadFormUrl.value; - input_url.innerText = downloadFormUrl.value; + input_url.href = url; + input_url.innerText = url; p.appendChild(input_url) 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'; @@ -45,6 +96,7 @@ function downloadFormButtonHandler(event) { p.appendChild(document.createTextNode('.')); popingNoticeContent.appendChild(p); popingNotice.classList.add('active'); + return false; }); } @@ -86,7 +138,10 @@ async function generateBlobVideoByProxy(url) { 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', { method: 'POST', mode: 'cors', @@ -94,7 +149,7 @@ async function getRealURL(url) { headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({url: url}), + body: JSON.stringify(request), }); return response.json(); } @@ -118,14 +173,23 @@ window.addEventListener('load', (event) => { downloadVideoPrepare = document.querySelector('#download-video-prepare'); closeAndResetVideoContainer = document.querySelector('#close-and-reset-video-container'); modalLoading = document.querySelector('#modal-loading'); - popingNotice = document.querySelector('.poping-notice'); - popingNoticeContent = document.querySelector('.poping-notice-content'); - closePopingNotice = document.querySelector('.close-poping-notice'); + popingNotice = document.querySelector('#poping-notice'); + popingNoticeContent = document.querySelector('#poping-notice-content'); + 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); downloadVideoPrepare.addEventListener('click', downloadVideoPrepareHandler); downloadForm.addEventListener('submit', downloadFormHandler); closeAndResetVideoContainer.addEventListener('click', closeAndResetVideoContainerHandler); + closeModalFormatSelector.addEventListener('click', (event) => { + modalFormatSelector.classList.remove('active'); + }); closePopingNotice.addEventListener('click', (event) => { popingNotice.classList.remove('active'); });