diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 74a3f65..0000000 --- a/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -# Author: @ale@ale.manalejandro.com Fixes by: Sergiotarxz. LICENSE: AGPLv3 - -FROM debian:sid -COPY . /peertube-dl -WORKDIR /peertube-dl -RUN apt update && apt -y install perl \ -build-essential \ -libwww-perl \ -liburi-encode-perl \ -libjson-perl \ -liblwp-protocol-https-perl \ -libtest-mockobject-perl \ -libtest-most-perl \ -libmojolicious-perl \ -libfile-mimeinfo-perl \ -cpanminus \ -duktape-dev \ -&& apt clean \ -&& perl Makefile.PL \ -&& make \ -&& make test \ -&& make install - -ENTRYPOINT [ "peertube-dl" ] diff --git a/Makefile.PL b/Makefile.PL index 965b185..06b144f 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -21,7 +21,8 @@ package MY { my $return = $self->SUPER::top_targets(@_); $return = [ split /\n/, $return ]; for my $i ( keys @$return ) { - $return->[$i] .= ' install_config' if $return->[$i] =~ /^all :/; + $return->[$i] .= ' install_config build_frontend install_frontend' + if $return->[$i] =~ /^all :/; } return join "\n", @$return; } @@ -42,6 +43,12 @@ THEME = ' . ( $config->{theme} // 'default' ) . "\n"; sub postamble { return "\n" + . "install_frontend : build_frontend\n" + . "\tif [ ! -e lib/Peertube/DL/public/ ];" + . " then mkdir -pv lib/Peertube/DL/public/;" . " fi\n" + . "\tcp -rfv themes/\${THEME}/public/dist/* lib/Peertube/DL/public/\n" + . "build_frontend :\n" + . "\tcd themes/\${THEME}/public && yarn install && yarn build\n" . "install_config :\n" . "\tinstall peertube-dl-web.conf bin/peertube-dl-web.conf\n" . "src: src/Makefile\n" diff --git a/README.md b/README.md index ade98ca..645111d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ youtube.com ### Localized install with cpanminus. ``` -sudo apt install cpanminus ducktape-dev +sudo apt install cpanminus ducktape-dev yarn cpanm . --installdeps cpanm . ``` diff --git a/lib/Peertube/DL/public/css/index.css b/lib/Peertube/DL/public/css/index.css index fa83ef8..d36581e 100644 --- a/lib/Peertube/DL/public/css/index.css +++ b/lib/Peertube/DL/public/css/index.css @@ -29,6 +29,10 @@ a:hover,a:focus { border: black 1px solid; } +#modal-video-container.active { + display: block; +} + .video-container-bar { display: flex; justify-content: right; @@ -245,6 +249,7 @@ h2 { text-decoration: none; color: black; background: #eee; + overflow-wrap: anywhere; } .format-list > div > a:hover { diff --git a/lib/Peertube/DL/public/js/peertube-dl-web.js b/lib/Peertube/DL/public/js/peertube-dl-web.js index a66965a..f035de2 100644 --- a/lib/Peertube/DL/public/js/peertube-dl-web.js +++ b/lib/Peertube/DL/public/js/peertube-dl-web.js @@ -1,200 +1,2 @@ -"use strict"; - -let downloadFormButton; -let modalVideoContainer; -let closeAndResetVideoContainer; -let downloadFormUrl; -let downloadVideo; -let downloadVideoPrepare; -let downloadVideoLoading; -let downloadForm; -let video; -let modalLoading; -let filename; -let url_video; -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'); - 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_video = 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 = 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'; - let issues_url_element = document.createElement('a'); - issues_url_element.href = issues_url; - issues_url_element.innerText = 'here'; - p.appendChild(issues_url_element); - p.appendChild(document.createTextNode('.')); - popingNoticeContent.appendChild(p); - popingNotice.classList.add('active'); - return false; - }); -} - -function downloadVideoPrepareHandler(event) { - downloadVideoPrepare.classList.remove('active'); - downloadVideoLoading.classList.add('active'); - generateBlobVideo(url_video).then( blob => { - downloadVideo.href = URL.createObjectURL(blob); - downloadVideo.download = filename; - downloadVideoLoading.classList.remove('active'); - downloadVideo.classList.add('active'); - }); -} - -function downloadFormHandler(event) { - downloadFormButtonHandler(event); -} - -async function generateBlobVideo(url) { - const blob = await fetch(url, { - mode: 'nocors', - }) - .then(res => res.blob()) - .catch( err => generateBlobVideoByProxy(url) ); - return blob; -} - -async function 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; - } - -async function getRealURL(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(); -} - -function closeAndResetVideoContainerHandler(event) { - event.preventDefault(); - modalVideoContainer.style.display = 'none'; - downloadVideo.classList.remove('active'); - downloadVideoLoading.classList.remove('active'); - downloadVideoPrepare.classList.add('active'); -} - -window.addEventListener('load', (event) => { - downloadFormButton = document.querySelector('#download-form-button'); - downloadForm = document.querySelector('#download-form'); - modalVideoContainer = document.querySelector('#modal-video-container'); - video = document.querySelector('#video'); - downloadFormUrl = document.querySelector('#download-form-url'); - downloadVideo = document.querySelector('#download-video'); - downloadVideoLoading = document.querySelector('#download-video-loading'); - 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'); - 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'); - }); - - popingNotice.classList.add('active'); -}); - - +(()=>{"use strict";class e{constructor(){this.query_selector="#poping-notice",this.closePopingNotice.addEventListener("click",(e=>{this.setVisible(!1)}))}setVisible(e){e?this.element.classList.add("active"):this.element.classList.remove("active")}setMessage(e){if(!e instanceof Array)throw"Message is not instance of Array.";let t=document.createElement("p");for(let o of e)if("string"==typeof o||o instanceof String)o=document.createTextNode(o),t.appendChild(o);else{if(!(o instanceof Node))throw"Node is not a instance of Node nor a String";t.appendChild(o)}this.popingNoticeContent.innerHTML="",this.popingNoticeContent.appendChild(t)}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")}}class t{constructor(e){this.query_selector="#download-form",this.callback=t=>{t.preventDefault(),e(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)}}class o{constructor(){this.query_selector="#modal-loading"}setVisible(e){e?this.element.classList.add("active"):this.element.classList.remove("active")}get element(){return document.querySelector(this.querySelector)}get querySelector(){return this.query_selector}}class i{constructor(){this.query_selector="#modal-video-container",this.addEventListeners()}setVisible(e){e?this.element.classList.add("active"):(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",(e=>{this.setVisible(!1)}))}downloadPrepareHandler(e){this.downloadVideoPrepare.classList.remove("active"),this.downloadVideoLoading.classList.add("active"),this.generateBlobVideo(this.URLVideo).then((e=>{this.downloadVideo.href=URL.createObjectURL(e),this.downloadVideo.download=this.filename,this.downloadVideoLoading.classList.remove("active"),this.downloadVideo.classList.add("active")}))}async generateBlobVideo(e){return await fetch(e,{mode:"cors"}).then((e=>e.blob())).catch((t=>this.generateBlobVideoByProxy(e)))}async generateBlobVideoByProxy(e){return await fetch("/proxy_to_get",{method:"POST",mode:"cors",cache:"no-cache",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:e})}).then((e=>e.blob()))}onCanPlay(e){video.addEventListener("canplay",(t=>{e()}))}setURLVideo(e){this.url_video=e,video.src=e}setFilename(e){this.filename=e}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}}class r{constructor(){this.query_selector="#modal-format-selector",this.addEventListeners()}appendFormat(e,t,o,i){let r=document.createElement("a");r.innerText=o?"Id: "+t.id+"\nFormat: "+t.mimeType+"\nQualityLabel: "+t.qualityLabel+"p\nBitrate: "+t.bitrate+"\n"+(void 0!==t.audioSampleRate?"AudioSampleRate: "+t.audioSampleRate+".\n":"No audio."):"Id: "+t.id+"\nFormat: "+t.mimeType+"\nAudioSampleRate: "+t.audioSampleRate+"\nBitrate: "+t.bitrate+".\n",r.addEventListener("click",(e=>{i(t.id)})),e.appendChild(r)}prepareFormatSelector(e,t,o,i,r){this.titleFormatSelector.innerText=e,this.descriptionFormatSelector.innerText=t,this.videoFormats.innerHTML="",this.audioFormats.innerHTML="";for(let e of o)this.appendFormat(this.audioFormats,e,!1,r);for(let e of i)this.appendFormat(this.videoFormats,e,!0,r)}setVisible(e){e?this.element.classList.add("active"):this.element.classList.remove("active")}addEventListeners(){this.closeFormatSelector.addEventListener("click",(e=>{this.setVisible(!1)}))}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}}class s{constructor(){this.poping_notice=new e,this.download_form=new t(this.onDownloadFormGot.bind(this)),this.loading_modal=new o,this.video_container=new i,this.format_selector=new r}init(){this.popingNotice.setVisible(!0)}onDownloadFormGot(e){this.dispatchURL(e)}dispatchURL(e,t){let o;this.loadingModal.setVisible(!0),this.queryAPI(e,t).then((t=>{if(void 0!==t.options&&void 0!==t.options.list_formats&&t.options.list_formats){if(void 0===t.formats||void 0===t.formats.audio_formats||void 0===t.formats.video_formats)throw"Format object is not valid.";this.formatSelector.prepareFormatSelector(t.title,t.description,t.formats.audio_formats,t.formats.video_formats,(t=>{this.loadingModal.setVisible(!0),this.dispatchURL(e,t)})),this.formatSelector.setVisible(!0),this.loadingModal.setVisible(!1)}else this.videoContainer.onCanPlay(this.onCanPlayVideoContainer.bind(this)),this.videoContainer.setURLVideo(t.url),this.videoContainer.setFilename(t.filename)})).catch((t=>{o=t.toString(),this.loadingModal.setVisible(!1);let i=document.createElement("a");i.href=e,i.innerText=e;let r=document.createElement("a");r.href="https://gitea.sergiotarxz.freemyip.com/sergiotarxz/Peertube-dl/issues",r.innerText="here",this.popingNotice.setMessage(["The url ",i," is not supported, the error was: ",o," if you think this is an error, report it ",r,"."]),this.popingNotice.setVisible(!0)}))}onCanPlayVideoContainer(){this.videoContainer.setVisible(!0),this.loadingModal.setVisible(!1)}async queryAPI(e,t){let o={url:e};return void 0!==t&&(o.format=t),(await fetch("/api",{method:"POST",mode:"cors",cache:"no-cache",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)})).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}}window.addEventListener("load",(e=>{(new s).init()}))})(); +//# sourceMappingURL=peertube-dl-web.js.map \ No newline at end of file diff --git a/themes/default/public/dist/css/index.css b/themes/default/public/dist/css/index.css new file mode 100644 index 0000000..d36581e --- /dev/null +++ b/themes/default/public/dist/css/index.css @@ -0,0 +1,274 @@ +body { + height: 99.9%; + margin: 0; + padding: 0; +} + +a { + color: blue; +} + +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; +} + +.application-container { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + text-align: center; + height: 100%; + background: #000; + color: white; +} + +#download-form { + display: flex; + justify-content: center; + flex-direction: column; +} + +#download-form-button { + margin-top: 5px; + height: 50px; + font-size: 1.5rem; + background: #fff; + color: black; + border: none; +} + +h2 { + font-size: 2rem; +} + +#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%; +} + +#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; +} + +#poping-notice.active { + display: block; +} + +#poping-notice-container-bar { + display: flex; + justify-content: center; + font-size: 5rem; +} + +#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; +} + +#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; + 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; + } +} diff --git a/themes/default/public/dist/index.html b/themes/default/public/dist/index.html new file mode 100644 index 0000000..4eac01f --- /dev/null +++ b/themes/default/public/dist/index.html @@ -0,0 +1,68 @@ + +
+ + + + + +Example description
+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.
+ +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.
+